fix(vk, android) Workaround for Vulkan driver pipeline linking issues (#10756) 39741ac0c8

* Workaround for Android 9/10 Adreno 5- and 6-series Vulkan driver bug

There's a bug in some of the early Adreno drivers where some of the Rive shaders will fail to compile due to hitting an internal limit. The workaround is to run a shader compilation pre-pass to inline functions before embedding the compiled shaders into the runtime.

Co-authored-by: Josh Jersild <joshua@rive.app>
This commit is contained in:
JoshJRive
2025-10-21 18:39:09 +00:00
parent 52632f65a2
commit e1494304d6
8 changed files with 316 additions and 165 deletions

View File

@@ -1 +1 @@
5dd872763be431796445c3b83d83d368b37991ea
39741ac0c89ef4af6214bf2ec0f2130e489d8ed0

View File

@@ -124,8 +124,8 @@ rive::Span<const uint32_t> loadNewShaderFileData()
riveSpirvPath / "draw_msaa_image_mesh.noclipdistance_vert.spirv",
riveSpirvPath / "draw_msaa_image_mesh.frag.spirv",
riveSpirvPath / "draw_msaa_image_mesh.fixedcolor_frag.spirv",
riveSpirvPath / "copy_attachment_to_attachment.vert",
riveSpirvPath / "copy_attachment_to_attachment.frag",
riveSpirvPath / "copy_attachment_to_attachment.vert.spirv",
riveSpirvPath / "copy_attachment_to_attachment.frag.spirv",
};
constexpr size_t numFiles = std::size(spirvFileNames);

View File

@@ -8,21 +8,13 @@ FLAGS :=
## Shader minification.
MINIFY_INPUTS := $(wildcard *.glsl) $(wildcard *.vert) $(wildcard *.frag)
MINIFY_EXPORT_OUTPUTS := $(addprefix $(OUT)/,\
$(patsubst %.glsl, %.glsl.exports.h,\
$(patsubst %.vert, %.vert.exports.h,\
$(patsubst %.frag, %.frag.exports.h,\
$(MINIFY_INPUTS)))))
MINIFY_EXPORT_OUTPUTS := $(addprefix $(OUT)/, $(addsuffix .exports.h, $(MINIFY_INPUTS)))
MINIFY_GLSL_OUTPUTS := $(addprefix $(OUT)/,\
$(patsubst %.glsl, %.minified.glsl,\
$(patsubst %.vert, %.minified.vert,\
$(patsubst %.frag, %.minified.frag,\
$(MINIFY_INPUTS)))))
MINIFY_HPP_OUTPUTS := $(addprefix $(OUT)/,\
$(patsubst %.glsl, %.glsl.hpp,\
$(patsubst %.vert, %.vert.hpp,\
$(patsubst %.frag, %.frag.hpp,\
$(MINIFY_INPUTS)))))
MINIFY_HPP_OUTPUTS := $(addprefix $(OUT)/, $(addsuffix .hpp, $(MINIFY_INPUTS)))
MINIFY_OUTPUTS := $(MINIFY_EXPORT_OUTPUTS) $(MINIFY_GLSL_OUTPUTS) $(MINIFY_HPP_OUTPUTS)
MINIFY_STAMP := $(OUT)/glsl.stamp
@@ -38,16 +30,23 @@ $(MINIFY_STAMP): $(MINIFY_INPUTS) minify.py
python3 minify.py $(FLAGS) -o $(OUT) $(MINIFY_INPUTS)
@touch $(MINIFY_STAMP)
$(OUT)/.:
@mkdir -p $@
## Metal shader offline compiling.
$(OUT)/ios/.: | $(OUT)/.
@mkdir -p $@
$(OUT)/macosx/.: | $(OUT)/.
@mkdir -p $@
DRAW_COMBINATIONS_METAL := $(OUT)/draw_combinations.metal
METAL_INPUTS := $(wildcard metal/*.metal)
METAL_MACOSX_AIR_OUTPUTS := \
$(addprefix $(OUT)/, $(patsubst metal/%.metal, macosx/%.air, $(METAL_INPUTS)))
METAL_IOS_AIR_OUTPUTS := $(addprefix $(OUT)/, $(patsubst metal/%.metal, ios/%.air, $(METAL_INPUTS)))
$(DRAW_COMBINATIONS_METAL): metal/generate_draw_combinations.py
@mkdir -p $(OUT)
$(DRAW_COMBINATIONS_METAL): metal/generate_draw_combinations.py | $(OUT)/.
python3 metal/generate_draw_combinations.py $(DRAW_COMBINATIONS_METAL)
rive_pls_macosx_metallib: $(OUT)/rive_pls_macosx.metallib.c
@@ -60,8 +59,7 @@ rive_renderer_appletvsimulator_metallib: $(OUT)/rive_renderer_appletvsimulator.m
## The source files all get regenerated in a batch, so there's no need to separate out separate
## rules for each intermediate file.
$(OUT)/macosx/rive_pls_macosx.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/macosx
$(OUT)/macosx/rive_pls_macosx.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/macosx/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk macosx metal -std=macos-metal2.3 \
-mmacosx-version-min=10.0 \
@@ -75,8 +73,7 @@ $(OUT)/rive_pls_macosx.metallib.c: $(OUT)/macosx/rive_pls_macosx.metallib
$(OUT)/macosx/rive_pls_macosx.metallib \
$(OUT)/rive_pls_macosx.metallib.c
$(OUT)/ios/rive_pls_ios.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_pls_ios.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk iphoneos metal -std=ios-metal2.2 \
-I$(OUT) -mios-version-min=13 -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -88,8 +85,7 @@ $(OUT)/ios/rive_pls_ios.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_
$(OUT)/rive_pls_ios.metallib.c: $(OUT)/ios/rive_pls_ios.metallib
xxd -i -n rive_pls_ios_metallib $(OUT)/ios/rive_pls_ios.metallib $(OUT)/rive_pls_ios.metallib.c
$(OUT)/ios/rive_pls_ios_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_pls_ios_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk iphonesimulator metal -std=ios-metal2.2 \
-I$(OUT) -miphonesimulator-version-min=13 -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -101,8 +97,7 @@ $(OUT)/ios/rive_pls_ios_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUT
$(OUT)/rive_pls_ios_simulator.metallib.c: $(OUT)/ios/rive_pls_ios_simulator.metallib
xxd -i -n rive_pls_ios_simulator_metallib $(OUT)/ios/rive_pls_ios_simulator.metallib $(OUT)/rive_pls_ios_simulator.metallib.c
$(OUT)/ios/rive_renderer_xros.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_renderer_xros.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk xros metal -std=metal3.1 \
-I$(OUT) --target=air64-apple-xros1.0 -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -114,8 +109,7 @@ $(OUT)/ios/rive_renderer_xros.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $
$(OUT)/rive_renderer_xros.metallib.c: $(OUT)/ios/rive_renderer_xros.metallib
xxd -i -n rive_renderer_xros_metallib $(OUT)/ios/rive_renderer_xros.metallib $(OUT)/rive_renderer_xros.metallib.c
$(OUT)/ios/rive_renderer_xros_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_renderer_xros_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk xrsimulator metal -std=metal3.1 \
-I$(OUT) --target=air64-apple-xros1.0-simulator -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -127,8 +121,7 @@ $(OUT)/ios/rive_renderer_xros_simulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL
$(OUT)/rive_renderer_xros_simulator.metallib.c: $(OUT)/ios/rive_renderer_xros_simulator.metallib
xxd -i -n rive_renderer_xros_simulator_metallib $(OUT)/ios/rive_renderer_xros_simulator.metallib $(OUT)/rive_renderer_xros_simulator.metallib.c
$(OUT)/ios/rive_renderer_appletvos.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_renderer_appletvos.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk appletvos metal -std=metal3.0 \
-I$(OUT) -mappletvos-version-min=16.0 -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -140,8 +133,7 @@ $(OUT)/ios/rive_renderer_appletvos.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPU
$(OUT)/rive_renderer_appletvos.metallib.c: $(OUT)/ios/rive_renderer_appletvos.metallib
xxd -i -n rive_renderer_appletvos_metallib $(OUT)/ios/rive_renderer_appletvos.metallib $(OUT)/rive_renderer_appletvos.metallib.c
$(OUT)/ios/rive_renderer_appletvsimulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL)
@mkdir -p $(OUT)/ios
$(OUT)/ios/rive_renderer_appletvsimulator.metallib: $(MINIFY_GLSL_OUTPUTS) $(METAL_INPUTS) $(DRAW_COMBINATIONS_METAL) | $(OUT)/ios/.
$(foreach FILE, $(METAL_INPUTS), \
xcrun -sdk appletvsimulator metal -std=metal3.0 \
-I$(OUT) -mappletvsimulator-version-min=16.0 -ffast-math -ffp-contract=fast -fpreserve-invariance \
@@ -155,117 +147,202 @@ $(OUT)/rive_renderer_appletvsimulator.metallib.c: $(OUT)/ios/rive_renderer_apple
## SPIRV compilation.
SPIRV_INPUTS := $(wildcard spirv/*.main) \
$(wildcard spirv/*.vert) \
$(wildcard spirv/*.frag)
SPIRV_OUTPUTS_HEADERS := \
$(addprefix $(OUT)/, $(patsubst %.main, %.vert.h, $(wildcard spirv/*.main))) \
$(addprefix $(OUT)/, $(patsubst %.main, %.frag.h, $(wildcard spirv/*.main))) \
$(addprefix $(OUT)/, $(patsubst %.vert, %.vert.h, $(wildcard spirv/*.vert))) \
$(addprefix $(OUT)/, $(patsubst %.frag, %.frag.h, $(wildcard spirv/*.frag))) \
$(OUT)/spirv/atomic_draw_image_mesh.fixedcolor_frag.h \
$(OUT)/spirv/atomic_draw_image_rect.fixedcolor_frag.h \
$(OUT)/spirv/atomic_draw_interior_triangles.fixedcolor_frag.h \
$(OUT)/spirv/atomic_draw_atlas_blit.fixedcolor_frag.h \
$(OUT)/spirv/atomic_draw_path.fixedcolor_frag.h \
$(OUT)/spirv/atomic_resolve.fixedcolor_frag.h \
$(OUT)/spirv/draw_msaa_atlas_blit.fixedcolor_frag.h \
$(OUT)/spirv/draw_msaa_image_mesh.fixedcolor_frag.h \
$(OUT)/spirv/draw_msaa_path.fixedcolor_frag.h \
$(OUT)/spirv/draw_msaa_stencil.fixedcolor_frag.h \
$(OUT)/spirv/draw_path.webgpu_vert.h \
$(OUT)/spirv/draw_path.webgpu_frag.h \
$(OUT)/spirv/draw_interior_triangles.webgpu_vert.h \
$(OUT)/spirv/draw_interior_triangles.webgpu_frag.h \
$(OUT)/spirv/draw_atlas_blit.webgpu_vert.h \
$(OUT)/spirv/draw_atlas_blit.webgpu_frag.h \
$(OUT)/spirv/draw_image_mesh.webgpu_frag.h \
$(OUT)/spirv/draw_msaa_path.noclipdistance_vert.h \
$(OUT)/spirv/draw_msaa_image_mesh.noclipdistance_vert.h \
$(OUT)/spirv/draw_msaa_atlas_blit.noclipdistance_vert.h \
$(OUT)/spirv/.: | $(OUT)/.
@mkdir -p $@
SPIRV_OUTPUTS_BINARY := \
$(addprefix $(OUT)/, $(patsubst %.main, %.vert.spirv, $(wildcard spirv/*.main))) \
$(addprefix $(OUT)/, $(patsubst %.main, %.frag.spirv, $(wildcard spirv/*.main))) \
$(addprefix $(OUT)/, $(patsubst %.vert, %.vert.spirv, $(wildcard spirv/*.vert))) \
$(addprefix $(OUT)/, $(patsubst %.frag, %.frag.spirv, $(wildcard spirv/*.frag))) \
$(OUT)/spirv/atomic_draw_image_mesh.fixedcolor_frag.spirv \
$(OUT)/spirv/atomic_draw_image_rect.fixedcolor_frag.spirv \
$(OUT)/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spirv \
$(OUT)/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spirv \
$(OUT)/spirv/atomic_draw_path.fixedcolor_frag.spirv \
$(OUT)/spirv/atomic_resolve.fixedcolor_frag.spirv \
$(OUT)/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spirv \
$(OUT)/spirv/draw_msaa_image_mesh.fixedcolor_frag.spirv \
$(OUT)/spirv/draw_msaa_path.fixedcolor_frag.spirv \
$(OUT)/spirv/draw_msaa_stencil.fixedcolor_frag.spirv \
$(OUT)/spirv/draw_msaa_path.noclipdistance_vert.spirv \
$(OUT)/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spirv \
$(OUT)/spirv/draw_msaa_image_mesh.noclipdistance_vert.spirv \
## Compile *.main into vertex shaders. First rule generates the binary spirv, then the .h file after.
$(OUT)/spirv/%.vert.spirv: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -I$(OUT) -V -o $@ $<
# All glsl source files that need to be built
SPIRV_STANDARD_INPUTS := $(wildcard spirv/*.main) \
$(wildcard spirv/*.vert) \
$(wildcard spirv/*.frag)
$(OUT)/spirv/%.vert.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -I$(OUT) -V --vn $(subst .main,_vert,$(notdir $<)) -o $@ $<
# Files that need separate fragment shaders with FIXED_FUNCTION_COLOR_OUTPUT defined.
SPIRV_FIXEDCOLOR_FRAG_INPUTS := \
spirv/atomic_draw_image_mesh.main \
spirv/atomic_draw_image_rect.main \
spirv/atomic_draw_interior_triangles.main \
spirv/atomic_draw_atlas_blit.main \
spirv/atomic_draw_path.main \
spirv/atomic_resolve.main \
spirv/draw_msaa_atlas_blit.main \
spirv/draw_msaa_image_mesh.main \
spirv/draw_msaa_path.main \
spirv/draw_msaa_stencil.main \
## Compile *.main again into fragment shaders.
$(OUT)/spirv/%.frag.spirv: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -I$(OUT) -V -o $@ $<
# Files that need separate vertex shaders that are built with DISABLE_CLIP_RECT_FOR_VULKAN_MSAA
SPIRV_NOCLIPDISTANCE_VERT_INPUTS := \
spirv/draw_msaa_path.main \
spirv/draw_msaa_image_mesh.main \
spirv/draw_msaa_atlas_blit.main \
$(OUT)/spirv/%.frag.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -I$(OUT) -V --vn $(subst .main,_frag,$(notdir $<)) -o $@ $<
# Files that need separate vertex shaders that are compatible with WebGPU
SPIRV_WEBGPU_VERT_INPUTS := \
spirv/draw_path.main \
spirv/draw_interior_triangles.main \
spirv/draw_atlas_blit.main \
## Compile atomic fragment shaders again with FIXED_FUNCTION_COLOR_OUTPUT defined.
$(OUT)/spirv/%.fixedcolor_frag.spirv: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -DFIXED_FUNCTION_COLOR_OUTPUT -I$(OUT) -V -o $@ $<
# Files that need separate fragment shaders that are compatible with WebGPU
SPIRV_WEBGPU_FRAG_INPUTS := \
spirv/draw_path.main \
spirv/draw_interior_triangles.main \
spirv/draw_atlas_blit.main \
spirv/draw_image_mesh.main \
$(OUT)/spirv/%.fixedcolor_frag.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -DFIXED_FUNCTION_COLOR_OUTPUT -I$(OUT) -V --vn $(subst .main,_fixedcolor_frag,$(notdir $<)) -o $@ $<
## Compile msaa vertex shaders again with DISABLE_CLIP_RECT_FOR_VULKAN_MSAA defined.
$(OUT)/spirv/%.noclipdistance_vert.spirv: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -DDISABLE_CLIP_RECT_FOR_VULKAN_MSAA -I$(OUT) -V -o $@ $<
$(OUT)/spirv/%.noclipdistance_vert.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -DDISABLE_CLIP_RECT_FOR_VULKAN_MSAA -I$(OUT) -V --vn $(subst .main,_noclipdistance_vert,$(notdir $<)) -o $@ $<
## Helpers for use in SPIRV_LIST_RULE (using lower_snake_case to distinguish things that use inputs like $1, $2, etc)
spirv_typed_filename = $(basename $1).$2
spirv_out_filename_no_ext = $(OUT)/$(call spirv_typed_filename,$1,$2)
spirv_type = $(lastword $(subst _, ,$2))
spirv_is_vert = $(findstring vert,$(spirv_type))
spirv_is_webgpu = $(findstring webgpu,$2)
spirv_is_atomic = $(findstring atomic,$1)
## Compile *.vert into vertex shaders.
$(OUT)/spirv/%.vert.spirv: spirv/%.vert $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -I$(OUT) -V -o $@ $<
## SPIR-V Optimizer settings
$(OUT)/spirv/%.vert.h: spirv/%.vert $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -I$(OUT) -V --vn $(subst .vert,_vert,$(notdir $<)) -o $@ $<
## To work around a driver bug in Android 9/10-era Adreno 5/6xx driver bugs, we need to run the
## fragment shaders through a preprocess optimization. The important bits for the workaround are:
## --merge-return
## --inline-entry-points-exhaustive
## without those, the pipelines on the affected devices will fail to link. However, we can do
## more optimizations while we're here, which
## Compile *.frag into fragment shaders.
$(OUT)/spirv/%.frag.spirv: spirv/%.frag $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -I$(OUT) -V -o $@ $<
## Vertex shaders can be optimized using the standard "optimize for performance" option
## with no known issues
SPIRV_STANDARD_VERT_OPT_PARAMS = -O
$(OUT)/spirv/%.frag.h: spirv/%.frag $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_SUBPASS_LOAD -I$(OUT) -V --vn $(subst .frag,_frag,$(notdir $<)) -o $@ $<
## Fragment shaders are a bit different: This is mostly a copy of the settings that spirv-opt
## reports for "-O" except with the --simplify-instructions option removed, which causes many
## issues on Adreno drivers. Additionally, the following three options cause problems with
## our atomic shaders:
## --ssa-rewrite
## --eliminate-local-single-block
## --eliminate-local-single-store
## So for atomic shaders, we end up with a different, even-more-pared-down set of instructions
## that doesn't drastically grow their sizes while also dodging all of the driver issues.
##
## Also use "-O" for the WebGPU shaders (for dawn) because something about the big list of
## options causes an internal compiler error error in its driver, but -O works great.
##
## TODO: Figure out why these cause issues with the atomic shaders and see if we can fix it
## to get consistent fragment shader optimizations.
spirv_frag_opt_params = \
$(if $(spirv_is_webgpu),-O, \
$(if $(spirv_is_atomic), \
--preserve-bindings \
--preserve-interface \
--wrap-opkill \
--simplify-instructions \
--eliminate-dead-branches \
--merge-return \
--inline-entry-points-exhaustive \
--eliminate-dead-inserts \
--eliminate-dead-members \
--merge-blocks \
--redundancy-elimination \
--cfg-cleanup \
--eliminate-dead-const \
--eliminate-dead-variables \
--eliminate-dead-functions \
--eliminate-dead-code-aggressive \
, \
--wrap-opkill \
--eliminate-dead-branches \
--merge-return \
--inline-entry-points-exhaustive \
--eliminate-dead-functions \
--eliminate-dead-code-aggressive \
--private-to-local \
--eliminate-local-single-block \
--eliminate-local-single-store \
--eliminate-dead-code-aggressive \
--scalar-replacement=100 \
--convert-local-access-chains \
--eliminate-local-single-block \
--eliminate-local-single-store \
--eliminate-dead-code-aggressive \
--ssa-rewrite \
--eliminate-dead-code-aggressive \
--ccp \
--eliminate-dead-code-aggressive \
--loop-unroll \
--eliminate-dead-branches \
--redundancy-elimination \
--combine-access-chains \
--scalar-replacement=100 \
--convert-local-access-chains \
--eliminate-local-single-block \
--eliminate-local-single-store \
--eliminate-dead-code-aggressive \
--ssa-rewrite \
--eliminate-dead-code-aggressive \
--vector-dce \
--eliminate-dead-inserts \
--eliminate-dead-branches \
--if-conversion \
--copy-propagate-arrays \
--reduce-load-size \
--eliminate-dead-code-aggressive \
--merge-blocks \
--redundancy-elimination \
--eliminate-dead-branches \
--merge-blocks \
) \
)
## Compile fragment shaders in a way that is compatible with webgpu.
$(OUT)/spirv/%.webgpu_vert.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S vert -DTARGET_VULKAN -DVERTEX -DSPEC_CONST_NONE -I$(OUT) -V --vn $(subst .main,_webgpu_vert,$(notdir $<)) -o $@ $<
## WebGPU fragment shaders need a different PLS_IMPL value
spirv_frag_params = $(if $(spirv_is_webgpu), -DPLS_IMPL_NONE, -DPLS_IMPL_SUBPASS_LOAD)
## Vertex shaders get the standard optimizations.
## Fragment shaders get the adreno workaround options if they're in the workaround list
## or they'll get the "atomic"
spirv_opt_params = \
$(if $(spirv_is_vert), \
$(SPIRV_STANDARD_VERT_OPT_PARAMS), \
$(spirv_frag_opt_params) \
) \
## The rules/outputs for a given input/output pair
## Usage: $(eval $(call spirv_list_rule, INPUT_FILENAME, OUTPUT_TYPE [, ADDITIONAL_COMPILE_OPTIONS]))
## Where OUTPUT_TYPE is, say, "vert" or "frag" or "fixedcolor_frag", etc
define spirv_list_rule
$(spirv_out_filename_no_ext).spirv: $1 $(MINIFY_STAMP) | $(OUT)/spirv/.
@glslangValidator -S $(spirv_type) -DTARGET_VULKAN \
$(if $(spirv_is_vert), -DVERTEX, -DFRAGMENT $(spirv_frag_params)) \
-I$(OUT) -V $3 -o $(spirv_out_filename_no_ext).spirv.unoptimized $1
@spirv-opt --preserve-bindings --preserve-interface $(spirv_opt_params) \
$(spirv_out_filename_no_ext).spirv.unoptimized -o $(spirv_out_filename_no_ext).spirv
@rm $(spirv_out_filename_no_ext).spirv.unoptimized
$(spirv_out_filename_no_ext).h: $(spirv_out_filename_no_ext).spirv
@python3 spirv_binary_to_header.py $(spirv_out_filename_no_ext).spirv $(spirv_out_filename_no_ext).h $(subst $(suffix $1),_$2,$(notdir $1))
SPIRV_OUTPUTS_BINARY += $(spirv_out_filename_no_ext).spirv
SPIRV_OUTPUTS_HEADERS += $(spirv_out_filename_no_ext).h
endef
## Make a set of rules for a given set of files and output types
## Usage: $(eval $(call make_spirv_rules, LIST_OF_INPUT_FILES, LIST_OF_OUTPUT_TYPES [, ADDITIONAL_COMPILE_OPTIONS]))
## Where LIST_OF_OUTPUT_TYPES can contain one or more of entries like "vert" or "frag" or "fixedcolor_frag"
define make_spirv_rules
## Note that the inner foreach will filter out ".frag" files from the list for any vert targets, and vice versa.
$(foreach type,$2,\
$(foreach file,$(filter-out %.$(if $(findstring vert, $(type)),frag,vert),$1),\
$(eval $(call spirv_list_rule,$(file),$(type),$3))\
)\
)
endef
## All .main/vert/frag files should build
$(eval $(call make_spirv_rules, $(SPIRV_STANDARD_INPUTS), vert frag))
## Each of the specialized SPIRV lists have their own associated rules
$(eval $(call make_spirv_rules, $(SPIRV_FIXEDCOLOR_FRAG_INPUTS), fixedcolor_frag, -DFIXED_FUNCTION_COLOR_OUTPUT))
$(eval $(call make_spirv_rules, $(SPIRV_NOCLIPDISTANCE_VERT_INPUTS), noclipdistance_vert, -DDISABLE_CLIP_RECT_FOR_VULKAN_MSAA))
$(eval $(call make_spirv_rules, $(SPIRV_WEBGPU_VERT_INPUTS), webgpu_vert, -DSPEC_CONST_NONE))
$(eval $(call make_spirv_rules, $(SPIRV_WEBGPU_FRAG_INPUTS), webgpu_frag, -DSPEC_CONST_NONE))
$(OUT)/spirv/%.webgpu_frag.h: spirv/%.main $(MINIFY_STAMP)
@mkdir -p $(OUT)/spirv
@glslangValidator -S frag -DTARGET_VULKAN -DFRAGMENT -DPLS_IMPL_NONE -DSPEC_CONST_NONE -I$(OUT) -V --vn $(subst .main,_webgpu_frag,$(notdir $<)) -o $@ $<
spirv: $(SPIRV_OUTPUTS_HEADERS)
spirv-binary: $(SPIRV_OUTPUTS_BINARY)
@@ -273,6 +350,9 @@ spirv-binary: $(SPIRV_OUTPUTS_BINARY)
## d3d compilation.
.PHONY: $(OUT)/d3d/render_atlas.frag.h
$(OUT)/d3d/.: | $(OUT)/.
@mkdir -p $@
FXC_DEBUG_FLAG := $(if $(filter --human-readable,$(FLAGS)),/Zi,)
@@ -283,24 +363,19 @@ D3D_OUTPUTS := \
$(OUT)/d3d/render_atlas_stroke.frag.h\
$(OUT)/d3d/render_atlas_fill.frag.h\
$(OUT)/d3d/%.vert.h: d3d/%.hlsl $(MINIFY_STAMP)
@mkdir -p $(OUT)/d3d
$(OUT)/d3d/%.vert.h: d3d/%.hlsl $(MINIFY_STAMP) | $(OUT)/d3d/.
@fxc /D VERTEX /I $(OUT) $(FXC_DEBUG_FLAG) /T vs_5_0 /Fh $@ $<
$(OUT)/d3d/%.frag.h: d3d/%.hlsl $(MINIFY_STAMP)
@mkdir -p $(OUT)/d3d
$(OUT)/d3d/%.frag.h: d3d/%.hlsl $(MINIFY_STAMP) | $(OUT)/d3d/.
@fxc /D FRAGMENT /I $(OUT) $(FXC_DEBUG_FLAG) /T ps_5_0 /Fh $@ $<
$(OUT)/d3d/render_atlas_stroke.frag.h: d3d/render_atlas.hlsl $(MINIFY_STAMP)
@mkdir -p $(OUT)/d3d
$(OUT)/d3d/render_atlas_stroke.frag.h: d3d/render_atlas.hlsl $(MINIFY_STAMP) | $(OUT)/d3d/.
@fxc /D FRAGMENT /D ATLAS_FEATHERED_STROKE $(FXC_DEBUG_FLAG) /I $(OUT) /T ps_5_0 /Fh $@ $<
$(OUT)/d3d/render_atlas_fill.frag.h: d3d/render_atlas.hlsl $(MINIFY_STAMP)
@mkdir -p $(OUT)/d3d
$(OUT)/d3d/render_atlas_fill.frag.h: d3d/render_atlas.hlsl $(MINIFY_STAMP) | $(OUT)/d3d/.
@fxc /D FRAGMENT /D ATLAS_FEATHERED_FILL $(FXC_DEBUG_FLAG) /I $(OUT) /T ps_5_0 /Fh $@ $<
$(OUT)/d3d/root.sig.h: d3d/root.sig
@mkdir -p $(OUT)/d3d
$(OUT)/d3d/root.sig.h: d3d/root.sig | $(OUT)/d3d/.
@fxc /I $(OUT) /T rootsig_1_1 /E ROOT_SIG /Fh $@ $<
d3d: $(D3D_OUTPUTS)

View File

@@ -158,13 +158,15 @@ half3 advanced_blend_coeffs(half3 src, half4 dstPremul, ushort mode)
break;
case BLEND_MODE_OVERLAY:
{
for (int i = 0; i < 3; ++i)
{
if (dst[i] <= .5)
coeffs[i] = 2. * src[i] * dst[i];
else
coeffs[i] = 1. - 2. * (1. - src[i]) * (1. - dst[i]);
}
// This logic is equivalent to the following, but should be more
// efficient, and works around a Vulkan Adreno 6-series Android 9/10
// driver bug:
// f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5
// 1-2*(1-Cs)*(1-Cd), otherwise
half3 sd = src * dst;
coeffs = 2.0 * mix(sd,
src + dst - sd - 0.5,
greaterThan(dst, make_half3(0.5)));
break;
}
case BLEND_MODE_DARKEN:
@@ -197,30 +199,40 @@ half3 advanced_blend_coeffs(half3 src, half4 dstPremul, ushort mode)
}
case BLEND_MODE_HARDLIGHT:
{
for (int i = 0; i < 3; ++i)
{
if (src[i] <= .5)
coeffs[i] = 2. * src[i] * dst[i];
else
coeffs[i] = 1. - 2. * (1. - src[i]) * (1. - dst[i]);
}
// This logic is equivalent to the following, but should be more
// efficient, and works around a Vulkan Adreno 6-series Android 9/10
// driver bug:
// f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5
// 1-2*(1-Cs)*(1-Cd), otherwise
half3 sd = src * dst;
coeffs = 2.0 * mix(sd,
src + dst - sd - 0.5,
greaterThan(src, make_half3(0.5)));
break;
}
case BLEND_MODE_SOFTLIGHT:
{
// This logic is equivalent to the following, but should be more
// efficient, and works around a Vulkan Adreno 6-series Android 9/10
// driver bug:
// f(Cs,Cd) =
// Cd-(1-2*Cs)*Cd*(1-Cd),
// if Cs <= 0.5
// Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3),
// if Cs > 0.5 and Cd <= 0.25
// Cd+(2*Cs-1)*(sqrt(Cd)-Cd),
// if Cs > 0.5 and Cd > 0.25
for (int i = 0; i < 3; ++i)
{
if (src[i] <= 0.5)
coeffs[i] =
dst[i] - (1. - 2. * src[i]) * dst[i] * (1. - dst[i]);
else if (dst[i] <= .25)
coeffs[i] =
dst[i] + (2. * src[i] - 1.) * dst[i] *
((16. * dst[i] - 12.) * dst[i] + 3.);
coeffs[i] = (1.0 - dst[i]);
else if (dst[i] <= 0.25)
coeffs[i] = ((16.0 * dst[i] - 12.0) * dst[i] + 3.0);
else
coeffs[i] =
dst[i] + (2. * src[i] - 1.) * (sqrt(dst[i]) - dst[i]);
coeffs[i] = (inversesqrt(dst[i]) - 1.0);
}
coeffs = dst + dst * (2.0 * src - 1.0) * coeffs;
break;
}
case BLEND_MODE_DIFFERENCE:
@@ -299,8 +311,10 @@ INLINE half3 advanced_color_blend(half3 src, half4 dstPremul, ushort mode)
// Also, since (X,Y,Z) == 1, alpha simplifies to standard src-over
// rules: A = Ad * (1 - As) + As
half3 coeffs = advanced_blend_coeffs(src, dstPremul, mode);
half2 p = make_half2(dstPremul.a, 1. - dstPremul.a); // p2 cancelled to 0.
return MUL(make_half2x3(coeffs, src), p);
// Because p0 is (Ad), p1 is (1 - Ad), and p2 is 0, this is equivalent to
// that matrix multiply:
return mix(src, coeffs, make_half3(dstPremul.a));
}
#endif // ENABLE_ADVANCED_BLEND

View File

@@ -366,6 +366,18 @@ INLINE float2x2 inverse(float2x2 m)
return adjoint * (1. / determinant(m));
}
// mix() with a boolean type in glsl maps to the hlsl ternary operator
INLINE float mix(float x, float y, bool s) { return s ? y : x; }
INLINE float2 mix(float2 x, float2 y, bool2 s) { return s ? y : x; }
INLINE float3 mix(float3 x, float3 y, bool3 s) { return s ? y : x; }
INLINE float4 mix(float4 x, float4 y, bool4 s) { return s ? y : x; }
INLINE half mix(half x, half y, bool s) { return s ? y : x; }
INLINE half2 mix(half2 x, half2 y, bool2 s) { return s ? y : x; }
INLINE half3 mix(half3 x, half3 y, bool3 s) { return s ? y : x; }
INLINE half4 mix(half4 x, half4 y, bool4 s) { return s ? y : x; }
// Redirects for intrinsics that have different names in HLSL
INLINE float mix(float x, float y, float s) { return $lerp(x, y, s); }
@@ -373,10 +385,10 @@ INLINE float2 mix(float2 x, float2 y, float2 s) { return $lerp(x, y, s); }
INLINE float3 mix(float3 x, float3 y, float3 s) { return $lerp(x, y, s); }
INLINE float4 mix(float4 x, float4 y, float4 s) { return $lerp(x, y, s); }
INLINE half mix(half x, half y, half s) { return x + s * (y - x); }
INLINE half2 mix(half2 x, half2 y, half2 s) { return x + s * (y - x); }
INLINE half3 mix(half3 x, half3 y, half3 s) { return x + s * (y - x); }
INLINE half4 mix(half4 x, half4 y, half4 s) { return x + s * (y - x); }
INLINE half mix(half x, half y, half s) { return $lerp(x, y, s); }
INLINE half2 mix(half2 x, half2 y, half2 s) { return $lerp(x, y, s); }
INLINE half3 mix(half3 x, half3 y, half3 s) { return $lerp(x, y, s); }
INLINE half4 mix(half4 x, half4 y, half4 s) { return $lerp(x, y, s); }
INLINE float fract(float x) { return $frac(x); }
INLINE float2 fract(float2 x) { return $frac(x); }

View File

@@ -0,0 +1,29 @@
# This is a little utility to take a SPIR-V binary file and turn it into a header file
import struct
import sys
if (len(sys.argv) != 4):
print("Usage: python binary_to_header.py <input_binary_file_path> <output_header_file_path> <array_name>")
sys.exit(1)
inPath = sys.argv[1]
outPath = sys.argv[2]
arrayName = sys.argv[3]
# Read the entire file in as binary then convert it into an array of integers
with open(inPath, "rb") as file:
binaryData = file.read()
if (len(binaryData) % 4 != 0):
printf("ERROR: Shader binary length was expected to be a multiple of 32 bits")
sys.exit(1)
data = struct.unpack(f"<{len(binaryData)//4}I", binaryData)
with open(outPath, "w") as file:
file.write("#pragma once\n\n")
file.write(f"const uint32_t {arrayName}[] = {{")
for i,val in enumerate(data):
# Add newlines (and indents) every 8 elements
if (i % 8 == 0):
file.write("\n ")
file.write(f"{val:#010x},")
file.write("\n};\n")

View File

@@ -145,8 +145,8 @@ public:
m_windowSurface,
swapOpts);
m_androidWindowWidth = m_swapchain->width();
m_androidWindowHeight = m_swapchain->height();
m_androidWindowWidth = m_width = m_swapchain->width();
m_androidWindowHeight = m_height = m_swapchain->height();
m_renderTarget =
impl()->makeRenderTarget(m_width,

View File

@@ -28,6 +28,8 @@ using simd::clamp;
using simd::dot;
using simd::max;
using simd::min;
using simd::sqrt;
using std::sqrt;
using float3 = vec<3>;
using float2x2 = std::array<float2, 2>;
using half4 = float4;
@@ -93,6 +95,13 @@ template <int N> vec<N> mix(vec<N> a, vec<N> b, ivec<N> t)
return simd::if_then_else(t, b, a);
}
template <int N> vec<N> mix(vec<N> a, vec<N> b, vec<N> t)
{
// Do the lerp using this form which is always correct at the endpoints
// (t == 0 or 1)
return a * (1 - t) + b * t;
}
template <typename T, int N>
ivec<N> equal(simd::gvec<T, N> x, simd::gvec<T, N> y)
{
@@ -111,10 +120,22 @@ ivec<N> lessThanEqual(simd::gvec<T, N> x, simd::gvec<T, N> y)
return x <= y;
}
template <typename T, int N>
ivec<N> lessThan(simd::gvec<T, N> x, simd::gvec<T, N> y)
{
return x < y;
}
template <typename T, int N>
ivec<N> greaterThanEqual(simd::gvec<T, N> x, simd::gvec<T, N> y)
{
return x >= y;
}
template <typename T, int N>
ivec<N> greaterThan(simd::gvec<T, N> x, simd::gvec<T, N> y)
{
return x > y;
}
#endif