another try
This commit is contained in:
@@ -1,259 +1,259 @@
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(shader_recompiler STATIC
|
||||
backend/bindings.h
|
||||
backend/glasm/emit_glasm.cpp
|
||||
backend/glasm/emit_glasm.h
|
||||
backend/glasm/emit_glasm_barriers.cpp
|
||||
backend/glasm/emit_glasm_bitwise_conversion.cpp
|
||||
backend/glasm/emit_glasm_composite.cpp
|
||||
backend/glasm/emit_glasm_context_get_set.cpp
|
||||
backend/glasm/emit_glasm_control_flow.cpp
|
||||
backend/glasm/emit_glasm_convert.cpp
|
||||
backend/glasm/emit_glasm_floating_point.cpp
|
||||
backend/glasm/emit_glasm_image.cpp
|
||||
backend/glasm/emit_glasm_instructions.h
|
||||
backend/glasm/emit_glasm_integer.cpp
|
||||
backend/glasm/emit_glasm_logical.cpp
|
||||
backend/glasm/emit_glasm_memory.cpp
|
||||
backend/glasm/emit_glasm_not_implemented.cpp
|
||||
backend/glasm/emit_glasm_select.cpp
|
||||
backend/glasm/emit_glasm_shared_memory.cpp
|
||||
backend/glasm/emit_glasm_special.cpp
|
||||
backend/glasm/emit_glasm_undefined.cpp
|
||||
backend/glasm/emit_glasm_warp.cpp
|
||||
backend/glasm/glasm_emit_context.cpp
|
||||
backend/glasm/glasm_emit_context.h
|
||||
backend/glasm/reg_alloc.cpp
|
||||
backend/glasm/reg_alloc.h
|
||||
backend/glsl/emit_glsl.cpp
|
||||
backend/glsl/emit_glsl.h
|
||||
backend/glsl/emit_glsl_atomic.cpp
|
||||
backend/glsl/emit_glsl_barriers.cpp
|
||||
backend/glsl/emit_glsl_bitwise_conversion.cpp
|
||||
backend/glsl/emit_glsl_composite.cpp
|
||||
backend/glsl/emit_glsl_context_get_set.cpp
|
||||
backend/glsl/emit_glsl_control_flow.cpp
|
||||
backend/glsl/emit_glsl_convert.cpp
|
||||
backend/glsl/emit_glsl_floating_point.cpp
|
||||
backend/glsl/emit_glsl_image.cpp
|
||||
backend/glsl/emit_glsl_instructions.h
|
||||
backend/glsl/emit_glsl_integer.cpp
|
||||
backend/glsl/emit_glsl_logical.cpp
|
||||
backend/glsl/emit_glsl_memory.cpp
|
||||
backend/glsl/emit_glsl_not_implemented.cpp
|
||||
backend/glsl/emit_glsl_select.cpp
|
||||
backend/glsl/emit_glsl_shared_memory.cpp
|
||||
backend/glsl/emit_glsl_special.cpp
|
||||
backend/glsl/emit_glsl_undefined.cpp
|
||||
backend/glsl/emit_glsl_warp.cpp
|
||||
backend/glsl/glsl_emit_context.cpp
|
||||
backend/glsl/glsl_emit_context.h
|
||||
backend/glsl/var_alloc.cpp
|
||||
backend/glsl/var_alloc.h
|
||||
backend/spirv/emit_spirv.cpp
|
||||
backend/spirv/emit_spirv.h
|
||||
backend/spirv/emit_spirv_atomic.cpp
|
||||
backend/spirv/emit_spirv_barriers.cpp
|
||||
backend/spirv/emit_spirv_bitwise_conversion.cpp
|
||||
backend/spirv/emit_spirv_composite.cpp
|
||||
backend/spirv/emit_spirv_context_get_set.cpp
|
||||
backend/spirv/emit_spirv_control_flow.cpp
|
||||
backend/spirv/emit_spirv_convert.cpp
|
||||
backend/spirv/emit_spirv_floating_point.cpp
|
||||
backend/spirv/emit_spirv_image.cpp
|
||||
backend/spirv/emit_spirv_image_atomic.cpp
|
||||
backend/spirv/emit_spirv_instructions.h
|
||||
backend/spirv/emit_spirv_integer.cpp
|
||||
backend/spirv/emit_spirv_logical.cpp
|
||||
backend/spirv/emit_spirv_memory.cpp
|
||||
backend/spirv/emit_spirv_select.cpp
|
||||
backend/spirv/emit_spirv_shared_memory.cpp
|
||||
backend/spirv/emit_spirv_special.cpp
|
||||
backend/spirv/emit_spirv_undefined.cpp
|
||||
backend/spirv/emit_spirv_warp.cpp
|
||||
backend/spirv/spirv_emit_context.cpp
|
||||
backend/spirv/spirv_emit_context.h
|
||||
environment.h
|
||||
exception.h
|
||||
frontend/ir/abstract_syntax_list.h
|
||||
frontend/ir/attribute.cpp
|
||||
frontend/ir/attribute.h
|
||||
frontend/ir/basic_block.cpp
|
||||
frontend/ir/basic_block.h
|
||||
frontend/ir/breadth_first_search.h
|
||||
frontend/ir/condition.cpp
|
||||
frontend/ir/condition.h
|
||||
frontend/ir/flow_test.cpp
|
||||
frontend/ir/flow_test.h
|
||||
frontend/ir/ir_emitter.cpp
|
||||
frontend/ir/ir_emitter.h
|
||||
frontend/ir/microinstruction.cpp
|
||||
frontend/ir/modifiers.h
|
||||
frontend/ir/opcodes.cpp
|
||||
frontend/ir/opcodes.h
|
||||
frontend/ir/opcodes.inc
|
||||
frontend/ir/patch.cpp
|
||||
frontend/ir/patch.h
|
||||
frontend/ir/post_order.cpp
|
||||
frontend/ir/post_order.h
|
||||
frontend/ir/pred.h
|
||||
frontend/ir/program.cpp
|
||||
frontend/ir/program.h
|
||||
frontend/ir/reg.h
|
||||
frontend/ir/type.cpp
|
||||
frontend/ir/type.h
|
||||
frontend/ir/value.cpp
|
||||
frontend/ir/value.h
|
||||
frontend/maxwell/control_flow.cpp
|
||||
frontend/maxwell/control_flow.h
|
||||
frontend/maxwell/decode.cpp
|
||||
frontend/maxwell/decode.h
|
||||
frontend/maxwell/indirect_branch_table_track.cpp
|
||||
frontend/maxwell/indirect_branch_table_track.h
|
||||
frontend/maxwell/instruction.h
|
||||
frontend/maxwell/location.h
|
||||
frontend/maxwell/maxwell.inc
|
||||
frontend/maxwell/opcodes.cpp
|
||||
frontend/maxwell/opcodes.h
|
||||
frontend/maxwell/structured_control_flow.cpp
|
||||
frontend/maxwell/structured_control_flow.h
|
||||
frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp
|
||||
frontend/maxwell/translate/impl/atomic_operations_shared_memory.cpp
|
||||
frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
|
||||
frontend/maxwell/translate/impl/barrier_operations.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_extract.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_insert.cpp
|
||||
frontend/maxwell/translate/impl/branch_indirect.cpp
|
||||
frontend/maxwell/translate/impl/common_encoding.h
|
||||
frontend/maxwell/translate/impl/common_funcs.cpp
|
||||
frontend/maxwell/translate/impl/common_funcs.h
|
||||
frontend/maxwell/translate/impl/condition_code_set.cpp
|
||||
frontend/maxwell/translate/impl/double_add.cpp
|
||||
frontend/maxwell/translate/impl/double_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/double_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/double_min_max.cpp
|
||||
frontend/maxwell/translate/impl/double_multiply.cpp
|
||||
frontend/maxwell/translate/impl/double_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/exit_program.cpp
|
||||
frontend/maxwell/translate/impl/find_leading_one.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_floating_point.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_min_max.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multi_function.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_range_reduction.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_swizzled_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.h
|
||||
frontend/maxwell/translate/impl/half_floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/impl.cpp
|
||||
frontend/maxwell/translate/impl/impl.h
|
||||
frontend/maxwell/translate/impl/integer_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_add_three_input.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/integer_floating_point_conversion.cpp
|
||||
frontend/maxwell/translate/impl/integer_funnel_shift.cpp
|
||||
frontend/maxwell/translate/impl/integer_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/integer_popcount.cpp
|
||||
frontend/maxwell/translate/impl/integer_scaled_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_left.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_right.cpp
|
||||
frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp
|
||||
frontend/maxwell/translate/impl/internal_stage_buffer_entry_read.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.h
|
||||
frontend/maxwell/translate/impl/load_effective_address.cpp
|
||||
frontend/maxwell/translate/impl/load_store_attribute.cpp
|
||||
frontend/maxwell/translate/impl/load_store_local_shared.cpp
|
||||
frontend/maxwell/translate/impl/load_store_memory.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation_three_input.cpp
|
||||
frontend/maxwell/translate/impl/move_predicate_to_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register_to_predicate.cpp
|
||||
frontend/maxwell/translate/impl/move_special_register.cpp
|
||||
frontend/maxwell/translate/impl/not_implemented.cpp
|
||||
frontend/maxwell/translate/impl/output_geometry.cpp
|
||||
frontend/maxwell/translate/impl/pixel_load.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_register.cpp
|
||||
frontend/maxwell/translate/impl/select_source_with_predicate.cpp
|
||||
frontend/maxwell/translate/impl/surface_atomic_operations.cpp
|
||||
frontend/maxwell/translate/impl/surface_load_store.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gradient.cpp
|
||||
frontend/maxwell/translate/impl/texture_load.cpp
|
||||
frontend/maxwell/translate/impl/texture_load_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_mipmap_level.cpp
|
||||
frontend/maxwell/translate/impl/texture_query.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.h
|
||||
frontend/maxwell/translate/impl/video_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/video_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/video_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/vote.cpp
|
||||
frontend/maxwell/translate/impl/warp_shuffle.cpp
|
||||
frontend/maxwell/translate/translate.cpp
|
||||
frontend/maxwell/translate/translate.h
|
||||
frontend/maxwell/translate_program.cpp
|
||||
frontend/maxwell/translate_program.h
|
||||
host_translate_info.h
|
||||
ir_opt/collect_shader_info_pass.cpp
|
||||
ir_opt/constant_propagation_pass.cpp
|
||||
ir_opt/dead_code_elimination_pass.cpp
|
||||
ir_opt/dual_vertex_pass.cpp
|
||||
ir_opt/global_memory_to_storage_buffer_pass.cpp
|
||||
ir_opt/identity_removal_pass.cpp
|
||||
ir_opt/lower_fp16_to_fp32.cpp
|
||||
ir_opt/lower_int64_to_int32.cpp
|
||||
ir_opt/passes.h
|
||||
ir_opt/rescaling_pass.cpp
|
||||
ir_opt/ssa_rewrite_pass.cpp
|
||||
ir_opt/texture_pass.cpp
|
||||
ir_opt/verification_pass.cpp
|
||||
object_pool.h
|
||||
profile.h
|
||||
program_header.h
|
||||
runtime_info.h
|
||||
shader_info.h
|
||||
varying_state.h
|
||||
)
|
||||
|
||||
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
/W4
|
||||
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
-Werror=conversion
|
||||
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(shader_recompiler STATIC
|
||||
backend/bindings.h
|
||||
backend/glasm/emit_glasm.cpp
|
||||
backend/glasm/emit_glasm.h
|
||||
backend/glasm/emit_glasm_barriers.cpp
|
||||
backend/glasm/emit_glasm_bitwise_conversion.cpp
|
||||
backend/glasm/emit_glasm_composite.cpp
|
||||
backend/glasm/emit_glasm_context_get_set.cpp
|
||||
backend/glasm/emit_glasm_control_flow.cpp
|
||||
backend/glasm/emit_glasm_convert.cpp
|
||||
backend/glasm/emit_glasm_floating_point.cpp
|
||||
backend/glasm/emit_glasm_image.cpp
|
||||
backend/glasm/emit_glasm_instructions.h
|
||||
backend/glasm/emit_glasm_integer.cpp
|
||||
backend/glasm/emit_glasm_logical.cpp
|
||||
backend/glasm/emit_glasm_memory.cpp
|
||||
backend/glasm/emit_glasm_not_implemented.cpp
|
||||
backend/glasm/emit_glasm_select.cpp
|
||||
backend/glasm/emit_glasm_shared_memory.cpp
|
||||
backend/glasm/emit_glasm_special.cpp
|
||||
backend/glasm/emit_glasm_undefined.cpp
|
||||
backend/glasm/emit_glasm_warp.cpp
|
||||
backend/glasm/glasm_emit_context.cpp
|
||||
backend/glasm/glasm_emit_context.h
|
||||
backend/glasm/reg_alloc.cpp
|
||||
backend/glasm/reg_alloc.h
|
||||
backend/glsl/emit_glsl.cpp
|
||||
backend/glsl/emit_glsl.h
|
||||
backend/glsl/emit_glsl_atomic.cpp
|
||||
backend/glsl/emit_glsl_barriers.cpp
|
||||
backend/glsl/emit_glsl_bitwise_conversion.cpp
|
||||
backend/glsl/emit_glsl_composite.cpp
|
||||
backend/glsl/emit_glsl_context_get_set.cpp
|
||||
backend/glsl/emit_glsl_control_flow.cpp
|
||||
backend/glsl/emit_glsl_convert.cpp
|
||||
backend/glsl/emit_glsl_floating_point.cpp
|
||||
backend/glsl/emit_glsl_image.cpp
|
||||
backend/glsl/emit_glsl_instructions.h
|
||||
backend/glsl/emit_glsl_integer.cpp
|
||||
backend/glsl/emit_glsl_logical.cpp
|
||||
backend/glsl/emit_glsl_memory.cpp
|
||||
backend/glsl/emit_glsl_not_implemented.cpp
|
||||
backend/glsl/emit_glsl_select.cpp
|
||||
backend/glsl/emit_glsl_shared_memory.cpp
|
||||
backend/glsl/emit_glsl_special.cpp
|
||||
backend/glsl/emit_glsl_undefined.cpp
|
||||
backend/glsl/emit_glsl_warp.cpp
|
||||
backend/glsl/glsl_emit_context.cpp
|
||||
backend/glsl/glsl_emit_context.h
|
||||
backend/glsl/var_alloc.cpp
|
||||
backend/glsl/var_alloc.h
|
||||
backend/spirv/emit_spirv.cpp
|
||||
backend/spirv/emit_spirv.h
|
||||
backend/spirv/emit_spirv_atomic.cpp
|
||||
backend/spirv/emit_spirv_barriers.cpp
|
||||
backend/spirv/emit_spirv_bitwise_conversion.cpp
|
||||
backend/spirv/emit_spirv_composite.cpp
|
||||
backend/spirv/emit_spirv_context_get_set.cpp
|
||||
backend/spirv/emit_spirv_control_flow.cpp
|
||||
backend/spirv/emit_spirv_convert.cpp
|
||||
backend/spirv/emit_spirv_floating_point.cpp
|
||||
backend/spirv/emit_spirv_image.cpp
|
||||
backend/spirv/emit_spirv_image_atomic.cpp
|
||||
backend/spirv/emit_spirv_instructions.h
|
||||
backend/spirv/emit_spirv_integer.cpp
|
||||
backend/spirv/emit_spirv_logical.cpp
|
||||
backend/spirv/emit_spirv_memory.cpp
|
||||
backend/spirv/emit_spirv_select.cpp
|
||||
backend/spirv/emit_spirv_shared_memory.cpp
|
||||
backend/spirv/emit_spirv_special.cpp
|
||||
backend/spirv/emit_spirv_undefined.cpp
|
||||
backend/spirv/emit_spirv_warp.cpp
|
||||
backend/spirv/spirv_emit_context.cpp
|
||||
backend/spirv/spirv_emit_context.h
|
||||
environment.h
|
||||
exception.h
|
||||
frontend/ir/abstract_syntax_list.h
|
||||
frontend/ir/attribute.cpp
|
||||
frontend/ir/attribute.h
|
||||
frontend/ir/basic_block.cpp
|
||||
frontend/ir/basic_block.h
|
||||
frontend/ir/breadth_first_search.h
|
||||
frontend/ir/condition.cpp
|
||||
frontend/ir/condition.h
|
||||
frontend/ir/flow_test.cpp
|
||||
frontend/ir/flow_test.h
|
||||
frontend/ir/ir_emitter.cpp
|
||||
frontend/ir/ir_emitter.h
|
||||
frontend/ir/microinstruction.cpp
|
||||
frontend/ir/modifiers.h
|
||||
frontend/ir/opcodes.cpp
|
||||
frontend/ir/opcodes.h
|
||||
frontend/ir/opcodes.inc
|
||||
frontend/ir/patch.cpp
|
||||
frontend/ir/patch.h
|
||||
frontend/ir/post_order.cpp
|
||||
frontend/ir/post_order.h
|
||||
frontend/ir/pred.h
|
||||
frontend/ir/program.cpp
|
||||
frontend/ir/program.h
|
||||
frontend/ir/reg.h
|
||||
frontend/ir/type.cpp
|
||||
frontend/ir/type.h
|
||||
frontend/ir/value.cpp
|
||||
frontend/ir/value.h
|
||||
frontend/maxwell/control_flow.cpp
|
||||
frontend/maxwell/control_flow.h
|
||||
frontend/maxwell/decode.cpp
|
||||
frontend/maxwell/decode.h
|
||||
frontend/maxwell/indirect_branch_table_track.cpp
|
||||
frontend/maxwell/indirect_branch_table_track.h
|
||||
frontend/maxwell/instruction.h
|
||||
frontend/maxwell/location.h
|
||||
frontend/maxwell/maxwell.inc
|
||||
frontend/maxwell/opcodes.cpp
|
||||
frontend/maxwell/opcodes.h
|
||||
frontend/maxwell/structured_control_flow.cpp
|
||||
frontend/maxwell/structured_control_flow.h
|
||||
frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp
|
||||
frontend/maxwell/translate/impl/atomic_operations_shared_memory.cpp
|
||||
frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
|
||||
frontend/maxwell/translate/impl/barrier_operations.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_extract.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_insert.cpp
|
||||
frontend/maxwell/translate/impl/branch_indirect.cpp
|
||||
frontend/maxwell/translate/impl/common_encoding.h
|
||||
frontend/maxwell/translate/impl/common_funcs.cpp
|
||||
frontend/maxwell/translate/impl/common_funcs.h
|
||||
frontend/maxwell/translate/impl/condition_code_set.cpp
|
||||
frontend/maxwell/translate/impl/double_add.cpp
|
||||
frontend/maxwell/translate/impl/double_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/double_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/double_min_max.cpp
|
||||
frontend/maxwell/translate/impl/double_multiply.cpp
|
||||
frontend/maxwell/translate/impl/double_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/exit_program.cpp
|
||||
frontend/maxwell/translate/impl/find_leading_one.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_floating_point.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_min_max.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multi_function.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_range_reduction.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_swizzled_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.h
|
||||
frontend/maxwell/translate/impl/half_floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/impl.cpp
|
||||
frontend/maxwell/translate/impl/impl.h
|
||||
frontend/maxwell/translate/impl/integer_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_add_three_input.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/integer_floating_point_conversion.cpp
|
||||
frontend/maxwell/translate/impl/integer_funnel_shift.cpp
|
||||
frontend/maxwell/translate/impl/integer_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/integer_popcount.cpp
|
||||
frontend/maxwell/translate/impl/integer_scaled_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_left.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_right.cpp
|
||||
frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp
|
||||
frontend/maxwell/translate/impl/internal_stage_buffer_entry_read.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.h
|
||||
frontend/maxwell/translate/impl/load_effective_address.cpp
|
||||
frontend/maxwell/translate/impl/load_store_attribute.cpp
|
||||
frontend/maxwell/translate/impl/load_store_local_shared.cpp
|
||||
frontend/maxwell/translate/impl/load_store_memory.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation_three_input.cpp
|
||||
frontend/maxwell/translate/impl/move_predicate_to_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register_to_predicate.cpp
|
||||
frontend/maxwell/translate/impl/move_special_register.cpp
|
||||
frontend/maxwell/translate/impl/not_implemented.cpp
|
||||
frontend/maxwell/translate/impl/output_geometry.cpp
|
||||
frontend/maxwell/translate/impl/pixel_load.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_register.cpp
|
||||
frontend/maxwell/translate/impl/select_source_with_predicate.cpp
|
||||
frontend/maxwell/translate/impl/surface_atomic_operations.cpp
|
||||
frontend/maxwell/translate/impl/surface_load_store.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gradient.cpp
|
||||
frontend/maxwell/translate/impl/texture_load.cpp
|
||||
frontend/maxwell/translate/impl/texture_load_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_mipmap_level.cpp
|
||||
frontend/maxwell/translate/impl/texture_query.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.h
|
||||
frontend/maxwell/translate/impl/video_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/video_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/video_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/vote.cpp
|
||||
frontend/maxwell/translate/impl/warp_shuffle.cpp
|
||||
frontend/maxwell/translate/translate.cpp
|
||||
frontend/maxwell/translate/translate.h
|
||||
frontend/maxwell/translate_program.cpp
|
||||
frontend/maxwell/translate_program.h
|
||||
host_translate_info.h
|
||||
ir_opt/collect_shader_info_pass.cpp
|
||||
ir_opt/constant_propagation_pass.cpp
|
||||
ir_opt/dead_code_elimination_pass.cpp
|
||||
ir_opt/dual_vertex_pass.cpp
|
||||
ir_opt/global_memory_to_storage_buffer_pass.cpp
|
||||
ir_opt/identity_removal_pass.cpp
|
||||
ir_opt/lower_fp16_to_fp32.cpp
|
||||
ir_opt/lower_int64_to_int32.cpp
|
||||
ir_opt/passes.h
|
||||
ir_opt/rescaling_pass.cpp
|
||||
ir_opt/ssa_rewrite_pass.cpp
|
||||
ir_opt/texture_pass.cpp
|
||||
ir_opt/verification_pass.cpp
|
||||
object_pool.h
|
||||
profile.h
|
||||
program_header.h
|
||||
runtime_info.h
|
||||
shader_info.h
|
||||
varying_state.h
|
||||
)
|
||||
|
||||
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
/W4
|
||||
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
-Werror=conversion
|
||||
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::Backend {
|
||||
|
||||
struct Bindings {
|
||||
u32 unified{};
|
||||
u32 uniform_buffer{};
|
||||
u32 storage_buffer{};
|
||||
u32 texture{};
|
||||
u32 image{};
|
||||
u32 texture_scaling_index{};
|
||||
u32 image_scaling_index{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::Backend {
|
||||
|
||||
struct Bindings {
|
||||
u32 unified{};
|
||||
u32 uniform_buffer{};
|
||||
u32 storage_buffer{};
|
||||
u32 texture{};
|
||||
u32 image{};
|
||||
u32 texture_scaling_index{};
|
||||
u32 image_scaling_index{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend
|
||||
|
||||
@@ -1,494 +1,494 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Identity {
|
||||
Identity(T data_) : data{data_} {}
|
||||
|
||||
T Extract() {
|
||||
return data;
|
||||
}
|
||||
|
||||
T data;
|
||||
};
|
||||
|
||||
template <bool scalar>
|
||||
class RegWrapper {
|
||||
public:
|
||||
RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
|
||||
const Value value{reg_alloc.Peek(ir_value)};
|
||||
if (value.type == Type::Register) {
|
||||
inst = ir_value.InstRecursive();
|
||||
reg = Register{value};
|
||||
} else {
|
||||
reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
|
||||
}
|
||||
switch (value.type) {
|
||||
case Type::Register:
|
||||
case Type::Void:
|
||||
break;
|
||||
case Type::U32:
|
||||
ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
|
||||
break;
|
||||
case Type::U64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto Extract() {
|
||||
if (inst) {
|
||||
reg_alloc.Unref(*inst);
|
||||
} else {
|
||||
reg_alloc.FreeReg(reg);
|
||||
}
|
||||
return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
IR::Inst* inst{};
|
||||
Register reg{};
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class ValueWrapper {
|
||||
public:
|
||||
ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
|
||||
: reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
|
||||
|
||||
ArgType Extract() {
|
||||
if (!ir_value.IsImmediate()) {
|
||||
reg_alloc.Unref(*ir_value.InstRecursive());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
const IR::Value& ir_value;
|
||||
ArgType value;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Register>) {
|
||||
return RegWrapper<false>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
|
||||
return RegWrapper<true>{ctx, arg};
|
||||
} else if constexpr (std::is_base_of_v<Value, ArgType>) {
|
||||
return ValueWrapper<ArgType>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return Identity<const IR::Value&>{arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return Identity{arg.U32()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return Identity{arg.Attribute()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return Identity{arg.Patch()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return Identity{arg.Reg()};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst>
|
||||
struct InvokeCall {
|
||||
template <typename... Args>
|
||||
InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, args.Extract()...);
|
||||
} else {
|
||||
func(ctx, args.Extract()...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (is_first_arg_inst) {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
|
||||
} else {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
const auto eval{
|
||||
[&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"IF NE.x;",
|
||||
eval(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("ENDIF;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("REP;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (!Settings::values.disable_shader_loop_safety_checks) {
|
||||
const u32 loop_index{ctx.num_safety_loop_vars++};
|
||||
const u32 vector_index{loop_index / 4};
|
||||
const char component{"xyzw"[loop_index % 4]};
|
||||
ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
|
||||
"BRK(LT.{});",
|
||||
vector_index, component, vector_index, component, component);
|
||||
}
|
||||
if (node.data.repeat.cond.IsImmediate()) {
|
||||
if (node.data.repeat.cond.U1()) {
|
||||
ctx.Add("ENDREP;");
|
||||
} else {
|
||||
ctx.Add("BRK;"
|
||||
"ENDREP;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK(EQ.x);"
|
||||
"ENDREP;",
|
||||
eval(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("BRK;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK (NE.x);",
|
||||
eval(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("RET;");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ctx.reg_alloc.IsEmpty()) {
|
||||
LOG_WARNING(Shader_GLASM, "Register leak after generating code");
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOptions(const IR::Program& program, const Profile& profile,
|
||||
const RuntimeInfo& runtime_info, std::string& header) {
|
||||
const Info& info{program.info};
|
||||
const Stage stage{program.stage};
|
||||
|
||||
// TODO: Track the shared atomic ops
|
||||
header += "OPTION NV_internal;"
|
||||
"OPTION NV_shader_storage_buffer;"
|
||||
"OPTION NV_gpu_program_fp64;";
|
||||
if (info.uses_int64_bit_atomics) {
|
||||
header += "OPTION NV_shader_atomic_int64;";
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
header += "OPTION NV_shader_atomic_float;";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
|
||||
header += "OPTION NV_shader_atomic_fp16_vector;";
|
||||
}
|
||||
if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
|
||||
info.uses_fswzadd) {
|
||||
header += "OPTION NV_shader_thread_group;";
|
||||
}
|
||||
if (info.uses_subgroup_shuffles) {
|
||||
header += "OPTION NV_shader_thread_shuffle;";
|
||||
}
|
||||
if (info.uses_sparse_residency) {
|
||||
header += "OPTION EXT_sparse_texture2;";
|
||||
}
|
||||
const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
|
||||
info.stores[IR::Attribute::Layer]};
|
||||
if ((stage != Stage::Geometry && stores_viewport_layer) ||
|
||||
info.stores[IR::Attribute::ViewportMask]) {
|
||||
if (profile.support_viewport_index_layer_non_geometry) {
|
||||
header += "OPTION NV_viewport_array2;";
|
||||
}
|
||||
}
|
||||
if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
|
||||
header += "OPTION NV_geometry_shader_passthrough;";
|
||||
}
|
||||
if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
|
||||
header += "OPTION EXT_shader_image_load_formatted;";
|
||||
}
|
||||
if (profile.support_derivative_control) {
|
||||
header += "OPTION ARB_derivative_control;";
|
||||
}
|
||||
if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
|
||||
header += "OPTION NV_early_fragment_tests;";
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
header += "OPTION ARB_draw_buffers;";
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view StageHeader(Stage stage) {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
return "!!NVvp5.0\n";
|
||||
case Stage::TessellationControl:
|
||||
return "!!NVtcp5.0\n";
|
||||
case Stage::TessellationEval:
|
||||
return "!!NVtep5.0\n";
|
||||
case Stage::Geometry:
|
||||
return "!!NVgp5.0\n";
|
||||
case Stage::Fragment:
|
||||
return "!!NVfp5.0\n";
|
||||
case Stage::Compute:
|
||||
return "!!NVcp5.0\n";
|
||||
}
|
||||
throw InvalidArgument("Invalid stage {}", stage);
|
||||
}
|
||||
|
||||
std::string_view InputPrimitive(InputTopology topology) {
|
||||
switch (topology) {
|
||||
case InputTopology::Points:
|
||||
return "POINTS";
|
||||
case InputTopology::Lines:
|
||||
return "LINES";
|
||||
case InputTopology::LinesAdjacency:
|
||||
return "LINES_ADJACENCY";
|
||||
case InputTopology::Triangles:
|
||||
return "TRIANGLES";
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return "TRIANGLES_ADJACENCY";
|
||||
}
|
||||
throw InvalidArgument("Invalid input topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
switch (topology) {
|
||||
case OutputTopology::PointList:
|
||||
return "POINTS";
|
||||
case OutputTopology::LineStrip:
|
||||
return "LINE_STRIP";
|
||||
case OutputTopology::TriangleStrip:
|
||||
return "TRIANGLE_STRIP";
|
||||
}
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view GetTessMode(TessPrimitive primitive) {
|
||||
switch (primitive) {
|
||||
case TessPrimitive::Triangles:
|
||||
return "TRIANGLES";
|
||||
case TessPrimitive::Quads:
|
||||
return "QUADS";
|
||||
case TessPrimitive::Isolines:
|
||||
return "ISOLINES";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation primitive {}", primitive);
|
||||
}
|
||||
|
||||
std::string_view GetTessSpacing(TessSpacing spacing) {
|
||||
switch (spacing) {
|
||||
case TessSpacing::Equal:
|
||||
return "EQUAL";
|
||||
case TessSpacing::FractionalOdd:
|
||||
return "FRACTIONAL_ODD";
|
||||
case TessSpacing::FractionalEven:
|
||||
return "FRACTIONAL_EVEN";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation spacing {}", spacing);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
std::string header{StageHeader(program.stage)};
|
||||
SetupOptions(program, profile, runtime_info, header);
|
||||
switch (program.stage) {
|
||||
case Stage::TessellationControl:
|
||||
header += fmt::format("VERTICES_OUT {};", program.invocations);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
header += fmt::format("TESS_MODE {};"
|
||||
"TESS_SPACING {};"
|
||||
"TESS_VERTEX_ORDER {};",
|
||||
GetTessMode(runtime_info.tess_primitive),
|
||||
GetTessSpacing(runtime_info.tess_spacing),
|
||||
runtime_info.tess_clockwise ? "CW" : "CCW");
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
|
||||
if (program.is_geometry_passthrough) {
|
||||
if (profile.support_geometry_shader_passthrough) {
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (program.info.passthrough.Generic(index)) {
|
||||
header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
|
||||
}
|
||||
}
|
||||
if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
|
||||
header += "PASSTHROUGH result.position;";
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
|
||||
}
|
||||
} else {
|
||||
header +=
|
||||
fmt::format("VERTICES_OUT {};"
|
||||
"PRIMITIVE_OUT {};",
|
||||
program.output_vertices, OutputPrimitive(program.output_topology));
|
||||
}
|
||||
break;
|
||||
case Stage::Compute:
|
||||
header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
|
||||
program.workgroup_size[1], program.workgroup_size[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (program.shared_memory_size > 0) {
|
||||
header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
|
||||
header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
|
||||
}
|
||||
if (program.info.uses_rescaling_uniform) {
|
||||
header += "PARAM scaling[1]={program.local[0..0]};";
|
||||
}
|
||||
header += "TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
|
||||
header += fmt::format("R{},", index);
|
||||
}
|
||||
if (program.local_memory_size > 0) {
|
||||
header += fmt::format("lmem[{}],", program.local_memory_size);
|
||||
}
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "FSWZA[4],FSWZB[4],";
|
||||
}
|
||||
const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("loop{},", index);
|
||||
}
|
||||
header += "RC;"
|
||||
"LONG TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
|
||||
header += fmt::format("D{},", index);
|
||||
}
|
||||
header += "DC;";
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "MOV.F FSWZA[0],-1;"
|
||||
"MOV.F FSWZA[1],1;"
|
||||
"MOV.F FSWZA[2],-1;"
|
||||
"MOV.F FSWZA[3],0;"
|
||||
"MOV.F FSWZB[0],-1;"
|
||||
"MOV.F FSWZB[1],-1;"
|
||||
"MOV.F FSWZB[2],1;"
|
||||
"MOV.F FSWZB[3],-1;";
|
||||
}
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
|
||||
}
|
||||
if (ctx.uses_y_direction) {
|
||||
header += "PARAM y_direction[1]={state.material.front.ambient};";
|
||||
}
|
||||
ctx.code.insert(0, header);
|
||||
ctx.code += "END";
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Identity {
|
||||
Identity(T data_) : data{data_} {}
|
||||
|
||||
T Extract() {
|
||||
return data;
|
||||
}
|
||||
|
||||
T data;
|
||||
};
|
||||
|
||||
template <bool scalar>
|
||||
class RegWrapper {
|
||||
public:
|
||||
RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
|
||||
const Value value{reg_alloc.Peek(ir_value)};
|
||||
if (value.type == Type::Register) {
|
||||
inst = ir_value.InstRecursive();
|
||||
reg = Register{value};
|
||||
} else {
|
||||
reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
|
||||
}
|
||||
switch (value.type) {
|
||||
case Type::Register:
|
||||
case Type::Void:
|
||||
break;
|
||||
case Type::U32:
|
||||
ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
|
||||
break;
|
||||
case Type::U64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto Extract() {
|
||||
if (inst) {
|
||||
reg_alloc.Unref(*inst);
|
||||
} else {
|
||||
reg_alloc.FreeReg(reg);
|
||||
}
|
||||
return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
IR::Inst* inst{};
|
||||
Register reg{};
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class ValueWrapper {
|
||||
public:
|
||||
ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
|
||||
: reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
|
||||
|
||||
ArgType Extract() {
|
||||
if (!ir_value.IsImmediate()) {
|
||||
reg_alloc.Unref(*ir_value.InstRecursive());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
const IR::Value& ir_value;
|
||||
ArgType value;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Register>) {
|
||||
return RegWrapper<false>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
|
||||
return RegWrapper<true>{ctx, arg};
|
||||
} else if constexpr (std::is_base_of_v<Value, ArgType>) {
|
||||
return ValueWrapper<ArgType>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return Identity<const IR::Value&>{arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return Identity{arg.U32()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return Identity{arg.Attribute()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return Identity{arg.Patch()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return Identity{arg.Reg()};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst>
|
||||
struct InvokeCall {
|
||||
template <typename... Args>
|
||||
InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, args.Extract()...);
|
||||
} else {
|
||||
func(ctx, args.Extract()...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (is_first_arg_inst) {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
|
||||
} else {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
const auto eval{
|
||||
[&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"IF NE.x;",
|
||||
eval(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("ENDIF;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("REP;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (!Settings::values.disable_shader_loop_safety_checks) {
|
||||
const u32 loop_index{ctx.num_safety_loop_vars++};
|
||||
const u32 vector_index{loop_index / 4};
|
||||
const char component{"xyzw"[loop_index % 4]};
|
||||
ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
|
||||
"BRK(LT.{});",
|
||||
vector_index, component, vector_index, component, component);
|
||||
}
|
||||
if (node.data.repeat.cond.IsImmediate()) {
|
||||
if (node.data.repeat.cond.U1()) {
|
||||
ctx.Add("ENDREP;");
|
||||
} else {
|
||||
ctx.Add("BRK;"
|
||||
"ENDREP;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK(EQ.x);"
|
||||
"ENDREP;",
|
||||
eval(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("BRK;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK (NE.x);",
|
||||
eval(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("RET;");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ctx.reg_alloc.IsEmpty()) {
|
||||
LOG_WARNING(Shader_GLASM, "Register leak after generating code");
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOptions(const IR::Program& program, const Profile& profile,
|
||||
const RuntimeInfo& runtime_info, std::string& header) {
|
||||
const Info& info{program.info};
|
||||
const Stage stage{program.stage};
|
||||
|
||||
// TODO: Track the shared atomic ops
|
||||
header += "OPTION NV_internal;"
|
||||
"OPTION NV_shader_storage_buffer;"
|
||||
"OPTION NV_gpu_program_fp64;";
|
||||
if (info.uses_int64_bit_atomics) {
|
||||
header += "OPTION NV_shader_atomic_int64;";
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
header += "OPTION NV_shader_atomic_float;";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
|
||||
header += "OPTION NV_shader_atomic_fp16_vector;";
|
||||
}
|
||||
if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
|
||||
info.uses_fswzadd) {
|
||||
header += "OPTION NV_shader_thread_group;";
|
||||
}
|
||||
if (info.uses_subgroup_shuffles) {
|
||||
header += "OPTION NV_shader_thread_shuffle;";
|
||||
}
|
||||
if (info.uses_sparse_residency) {
|
||||
header += "OPTION EXT_sparse_texture2;";
|
||||
}
|
||||
const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
|
||||
info.stores[IR::Attribute::Layer]};
|
||||
if ((stage != Stage::Geometry && stores_viewport_layer) ||
|
||||
info.stores[IR::Attribute::ViewportMask]) {
|
||||
if (profile.support_viewport_index_layer_non_geometry) {
|
||||
header += "OPTION NV_viewport_array2;";
|
||||
}
|
||||
}
|
||||
if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
|
||||
header += "OPTION NV_geometry_shader_passthrough;";
|
||||
}
|
||||
if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
|
||||
header += "OPTION EXT_shader_image_load_formatted;";
|
||||
}
|
||||
if (profile.support_derivative_control) {
|
||||
header += "OPTION ARB_derivative_control;";
|
||||
}
|
||||
if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
|
||||
header += "OPTION NV_early_fragment_tests;";
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
header += "OPTION ARB_draw_buffers;";
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view StageHeader(Stage stage) {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
return "!!NVvp5.0\n";
|
||||
case Stage::TessellationControl:
|
||||
return "!!NVtcp5.0\n";
|
||||
case Stage::TessellationEval:
|
||||
return "!!NVtep5.0\n";
|
||||
case Stage::Geometry:
|
||||
return "!!NVgp5.0\n";
|
||||
case Stage::Fragment:
|
||||
return "!!NVfp5.0\n";
|
||||
case Stage::Compute:
|
||||
return "!!NVcp5.0\n";
|
||||
}
|
||||
throw InvalidArgument("Invalid stage {}", stage);
|
||||
}
|
||||
|
||||
std::string_view InputPrimitive(InputTopology topology) {
|
||||
switch (topology) {
|
||||
case InputTopology::Points:
|
||||
return "POINTS";
|
||||
case InputTopology::Lines:
|
||||
return "LINES";
|
||||
case InputTopology::LinesAdjacency:
|
||||
return "LINES_ADJACENCY";
|
||||
case InputTopology::Triangles:
|
||||
return "TRIANGLES";
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return "TRIANGLES_ADJACENCY";
|
||||
}
|
||||
throw InvalidArgument("Invalid input topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
switch (topology) {
|
||||
case OutputTopology::PointList:
|
||||
return "POINTS";
|
||||
case OutputTopology::LineStrip:
|
||||
return "LINE_STRIP";
|
||||
case OutputTopology::TriangleStrip:
|
||||
return "TRIANGLE_STRIP";
|
||||
}
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view GetTessMode(TessPrimitive primitive) {
|
||||
switch (primitive) {
|
||||
case TessPrimitive::Triangles:
|
||||
return "TRIANGLES";
|
||||
case TessPrimitive::Quads:
|
||||
return "QUADS";
|
||||
case TessPrimitive::Isolines:
|
||||
return "ISOLINES";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation primitive {}", primitive);
|
||||
}
|
||||
|
||||
std::string_view GetTessSpacing(TessSpacing spacing) {
|
||||
switch (spacing) {
|
||||
case TessSpacing::Equal:
|
||||
return "EQUAL";
|
||||
case TessSpacing::FractionalOdd:
|
||||
return "FRACTIONAL_ODD";
|
||||
case TessSpacing::FractionalEven:
|
||||
return "FRACTIONAL_EVEN";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation spacing {}", spacing);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
std::string header{StageHeader(program.stage)};
|
||||
SetupOptions(program, profile, runtime_info, header);
|
||||
switch (program.stage) {
|
||||
case Stage::TessellationControl:
|
||||
header += fmt::format("VERTICES_OUT {};", program.invocations);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
header += fmt::format("TESS_MODE {};"
|
||||
"TESS_SPACING {};"
|
||||
"TESS_VERTEX_ORDER {};",
|
||||
GetTessMode(runtime_info.tess_primitive),
|
||||
GetTessSpacing(runtime_info.tess_spacing),
|
||||
runtime_info.tess_clockwise ? "CW" : "CCW");
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
|
||||
if (program.is_geometry_passthrough) {
|
||||
if (profile.support_geometry_shader_passthrough) {
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (program.info.passthrough.Generic(index)) {
|
||||
header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
|
||||
}
|
||||
}
|
||||
if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
|
||||
header += "PASSTHROUGH result.position;";
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
|
||||
}
|
||||
} else {
|
||||
header +=
|
||||
fmt::format("VERTICES_OUT {};"
|
||||
"PRIMITIVE_OUT {};",
|
||||
program.output_vertices, OutputPrimitive(program.output_topology));
|
||||
}
|
||||
break;
|
||||
case Stage::Compute:
|
||||
header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
|
||||
program.workgroup_size[1], program.workgroup_size[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (program.shared_memory_size > 0) {
|
||||
header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
|
||||
header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
|
||||
}
|
||||
if (program.info.uses_rescaling_uniform) {
|
||||
header += "PARAM scaling[1]={program.local[0..0]};";
|
||||
}
|
||||
header += "TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
|
||||
header += fmt::format("R{},", index);
|
||||
}
|
||||
if (program.local_memory_size > 0) {
|
||||
header += fmt::format("lmem[{}],", program.local_memory_size);
|
||||
}
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "FSWZA[4],FSWZB[4],";
|
||||
}
|
||||
const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("loop{},", index);
|
||||
}
|
||||
header += "RC;"
|
||||
"LONG TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
|
||||
header += fmt::format("D{},", index);
|
||||
}
|
||||
header += "DC;";
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "MOV.F FSWZA[0],-1;"
|
||||
"MOV.F FSWZA[1],1;"
|
||||
"MOV.F FSWZA[2],-1;"
|
||||
"MOV.F FSWZA[3],0;"
|
||||
"MOV.F FSWZB[0],-1;"
|
||||
"MOV.F FSWZB[1],-1;"
|
||||
"MOV.F FSWZB[2],1;"
|
||||
"MOV.F FSWZB[3],-1;";
|
||||
}
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
|
||||
}
|
||||
if (ctx.uses_y_direction) {
|
||||
header += "PARAM y_direction[1]={state.material.front.ambient};";
|
||||
}
|
||||
ctx.code.insert(0, header);
|
||||
ctx.code += "END";
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
constexpr u32 PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE = 1;
|
||||
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLASM(profile, runtime_info, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
constexpr u32 PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE = 1;
|
||||
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLASM(profile, runtime_info, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("BAR;");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR.CTA;");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("BAR;");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR.CTA;");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
static void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{RegAlloc::AliasInst(*value.Inst())};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real register out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const ScalarS32 input{ctx.reg_alloc.Consume(value)};
|
||||
if (ret != input) {
|
||||
ctx.Add("MOV.S {},{};", ret, input);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64.U {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64.U {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("PK2H {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP2H {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64 {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
static void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{RegAlloc::AliasInst(*value.Inst())};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real register out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const ScalarS32 input{ctx.reg_alloc.Consume(value)};
|
||||
if (ret != input) {
|
||||
ctx.Add("MOV.S {},{};", ret, input);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64.U {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64.U {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("PK2H {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP2H {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64 {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,243 +1,243 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <auto read_imm, char type, typename... Values>
|
||||
void CompositeConstruct(EmitContext& ctx, IR::Inst& inst, Values&&... elements) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (std::ranges::any_of(std::array{elements...},
|
||||
[](const IR::Value& value) { return value.IsImmediate(); })) {
|
||||
using Type = std::invoke_result_t<decltype(read_imm), IR::Value>;
|
||||
const std::array<Type, 4> values{(elements.IsImmediate() ? (elements.*read_imm)() : 0)...};
|
||||
ctx.Add("MOV.{} {},{{{},{},{},{}}};", type, ret, fmt::to_string(values[0]),
|
||||
fmt::to_string(values[1]), fmt::to_string(values[2]), fmt::to_string(values[3]));
|
||||
}
|
||||
size_t index{};
|
||||
for (const IR::Value& element : {elements...}) {
|
||||
if (!element.IsImmediate()) {
|
||||
const ScalarU32 value{ctx.reg_alloc.Consume(element)};
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, "xyzw"[index], value);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void CompositeExtract(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ret == composite && index == 0) {
|
||||
// No need to do anything here, the source and destination are the same register
|
||||
return;
|
||||
}
|
||||
ctx.Add("MOV.{} {}.x,{}.{};", type, ret, composite, "xyzw"[index]);
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
void CompositeInsert(EmitContext& ctx, IR::Inst& inst, Register composite, ObjectType object,
|
||||
u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const char swizzle{"xyzw"[index]};
|
||||
if (ret != composite && ret == object) {
|
||||
// The object is aliased with the return value, so we have to use a temporary to insert
|
||||
ctx.Add("MOV.{} RC,{};"
|
||||
"MOV.{} RC.{},{};"
|
||||
"MOV.{} {},RC;",
|
||||
type, composite, type, swizzle, object, type, ret);
|
||||
} else if (ret != composite) {
|
||||
// The input composite is not aliased with the return value so we have to copy it before
|
||||
// hand. But the insert object is not aliased with the return value, so we don't have to
|
||||
// worry about that
|
||||
ctx.Add("MOV.{} {},{};"
|
||||
"MOV.{} {}.{},{};",
|
||||
type, ret, composite, type, ret, swizzle, object);
|
||||
} else {
|
||||
// The return value is alised so we can just insert the object, it doesn't matter if it's
|
||||
// aliased
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3,
|
||||
[[maybe_unused]] Register e4) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <auto read_imm, char type, typename... Values>
|
||||
void CompositeConstruct(EmitContext& ctx, IR::Inst& inst, Values&&... elements) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (std::ranges::any_of(std::array{elements...},
|
||||
[](const IR::Value& value) { return value.IsImmediate(); })) {
|
||||
using Type = std::invoke_result_t<decltype(read_imm), IR::Value>;
|
||||
const std::array<Type, 4> values{(elements.IsImmediate() ? (elements.*read_imm)() : 0)...};
|
||||
ctx.Add("MOV.{} {},{{{},{},{},{}}};", type, ret, fmt::to_string(values[0]),
|
||||
fmt::to_string(values[1]), fmt::to_string(values[2]), fmt::to_string(values[3]));
|
||||
}
|
||||
size_t index{};
|
||||
for (const IR::Value& element : {elements...}) {
|
||||
if (!element.IsImmediate()) {
|
||||
const ScalarU32 value{ctx.reg_alloc.Consume(element)};
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, "xyzw"[index], value);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void CompositeExtract(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ret == composite && index == 0) {
|
||||
// No need to do anything here, the source and destination are the same register
|
||||
return;
|
||||
}
|
||||
ctx.Add("MOV.{} {}.x,{}.{};", type, ret, composite, "xyzw"[index]);
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
void CompositeInsert(EmitContext& ctx, IR::Inst& inst, Register composite, ObjectType object,
|
||||
u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const char swizzle{"xyzw"[index]};
|
||||
if (ret != composite && ret == object) {
|
||||
// The object is aliased with the return value, so we have to use a temporary to insert
|
||||
ctx.Add("MOV.{} RC,{};"
|
||||
"MOV.{} RC.{},{};"
|
||||
"MOV.{} {},RC;",
|
||||
type, composite, type, swizzle, object, type, ret);
|
||||
} else if (ret != composite) {
|
||||
// The input composite is not aliased with the return value so we have to copy it before
|
||||
// hand. But the insert object is not aliased with the return value, so we don't have to
|
||||
// worry about that
|
||||
ctx.Add("MOV.{} {},{};"
|
||||
"MOV.{} {}.{},{};",
|
||||
type, ret, composite, type, ret, swizzle, object);
|
||||
} else {
|
||||
// The return value is alised so we can just insert the object, it doesn't matter if it's
|
||||
// aliased
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3,
|
||||
[[maybe_unused]] Register e4) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,407 +1,407 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
|
||||
std::string_view size) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (offset.type == Type::U32) {
|
||||
// Avoid reading arrays out of bounds, matching hardware's behavior
|
||||
if (offset.imm_u32 >= 0x10'000) {
|
||||
ctx.Add("MOV.S {},0;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (binding.IsImmediate()) {
|
||||
ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
|
||||
return;
|
||||
}
|
||||
|
||||
const ScalarU32 idx{ctx.reg_alloc.Consume(binding)};
|
||||
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
|
||||
ctx.Add("SEQ.S.CC RC.x,{},{};"
|
||||
"IF NE.x;"
|
||||
"LDC.{} {},c{}[{}];",
|
||||
idx, i, size, ret, i, offset);
|
||||
|
||||
if (i != Info::MAX_INDIRECT_CBUFS - 1) {
|
||||
ctx.Add("ELSE;");
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U8");
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S8");
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U16");
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S16");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32");
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "F32");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32X2");
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,in_attr{}{}[0].{};", inst, index, VertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,{}.texcoord[{}].{};", inst, ctx.attrib_name, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
ctx.Add("MOV.F {}.x,vertex_position{}.{};", inst, VertexIndex(ctx, vertex), swizzle);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.x,{}.position.{};", inst, ctx.attrib_name, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F {}.x,{}.color.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.Add("MOV.F {}.x,{}.pointcoord.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value,
|
||||
[[maybe_unused]] ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F out_attr{}[0].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9R) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F result.texcoord[{}].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.layer.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Layer stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewport.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Viewport stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
// NV_viewport_array2 is required to access result.viewportmask, regardless of shader stage.
|
||||
if (ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewportmask[0].x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Device does not support storing to ViewportMask");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("MOV.F result.pointsize.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("MOV.F result.position.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F result.color.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("MOV.F result.color.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("MOV.F result.color.back.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("MOV.F result.color.back.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("MOV.F result.fogcoord.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("MOV.F result.clip[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex) {
|
||||
// RC.x = base_index
|
||||
// RC.y = masked_index
|
||||
// RC.z = compare_index
|
||||
ctx.Add("SHR.S RC.x,{},2;"
|
||||
"AND.S RC.y,RC.x,3;"
|
||||
"SHR.S RC.z,{},4;",
|
||||
offset, offset);
|
||||
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
u32 num_endifs{};
|
||||
const auto read{[&](u32 compare_index, const std::array<std::string, 4>& values) {
|
||||
++num_endifs;
|
||||
ctx.Add("SEQ.S.CC RC.w,RC.z,{};" // compare_index
|
||||
"IF NE.w;"
|
||||
// X
|
||||
"SEQ.S.CC RC.w,RC.y,0;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Y
|
||||
"SEQ.S.CC RC.w,RC.y,1;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Z
|
||||
"SEQ.S.CC RC.w,RC.y,2;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// W
|
||||
"MOV {}.x,{};"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ELSE;",
|
||||
compare_index, ret, values[0], ret, values[1], ret, values[2], ret, values[3]);
|
||||
}};
|
||||
const auto read_swizzled{[&](u32 compare_index, std::string_view value) {
|
||||
const std::array values{fmt::format("{}.x", value), fmt::format("{}.y", value),
|
||||
fmt::format("{}.z", value), fmt::format("{}.w", value)};
|
||||
read(compare_index, values);
|
||||
}};
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const u32 index{static_cast<u32>(IR::Attribute::PositionX)};
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex)));
|
||||
} else {
|
||||
read_swizzled(index, fmt::format("{}.position", ctx.attrib_name));
|
||||
}
|
||||
}
|
||||
for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
|
||||
if (!ctx.info.loads.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex)));
|
||||
}
|
||||
for (u32 i = 0; i < num_endifs; ++i) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarU32 offset,
|
||||
[[maybe_unused]] ScalarF32 value, [[maybe_unused]] ScalarU32 vertex) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
const std::string_view out{ctx.stage == Stage::TessellationControl ? ".out" : ""};
|
||||
ctx.Add("MOV.F {},primitive{}.patch.attrib[{}].{};", inst, out, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("MOV.F result.patch.attrib[{}].{},{};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
ctx.Add("MOV.F result.patch.tessouter[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("MOV.F result.patch.tessinner[0].x,{};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("MOV.F result.patch.tessinner[1].x,{};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value) {
|
||||
ctx.Add("MOV.F frag_color{}.{},{};", index, "xyzw"[component], value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value) {
|
||||
ctx.Add("MOV.S result.samplemask.x,{};", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
|
||||
ctx.Add("MOV.F result.depth.z,{};", value);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.groupid;", inst);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.localid;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
|
||||
ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value) {
|
||||
ctx.Add("MOV.U lmem[{}].x,{};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
|
||||
std::string_view size) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (offset.type == Type::U32) {
|
||||
// Avoid reading arrays out of bounds, matching hardware's behavior
|
||||
if (offset.imm_u32 >= 0x10'000) {
|
||||
ctx.Add("MOV.S {},0;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (binding.IsImmediate()) {
|
||||
ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
|
||||
return;
|
||||
}
|
||||
|
||||
const ScalarU32 idx{ctx.reg_alloc.Consume(binding)};
|
||||
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
|
||||
ctx.Add("SEQ.S.CC RC.x,{},{};"
|
||||
"IF NE.x;"
|
||||
"LDC.{} {},c{}[{}];",
|
||||
idx, i, size, ret, i, offset);
|
||||
|
||||
if (i != Info::MAX_INDIRECT_CBUFS - 1) {
|
||||
ctx.Add("ELSE;");
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U8");
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S8");
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U16");
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S16");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32");
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "F32");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32X2");
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,in_attr{}{}[0].{};", inst, index, VertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,{}.texcoord[{}].{};", inst, ctx.attrib_name, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
ctx.Add("MOV.F {}.x,vertex_position{}.{};", inst, VertexIndex(ctx, vertex), swizzle);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.x,{}.position.{};", inst, ctx.attrib_name, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F {}.x,{}.color.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.Add("MOV.F {}.x,{}.pointcoord.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value,
|
||||
[[maybe_unused]] ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F out_attr{}[0].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9R) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F result.texcoord[{}].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.layer.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Layer stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewport.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Viewport stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
// NV_viewport_array2 is required to access result.viewportmask, regardless of shader stage.
|
||||
if (ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewportmask[0].x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Device does not support storing to ViewportMask");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("MOV.F result.pointsize.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("MOV.F result.position.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F result.color.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("MOV.F result.color.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("MOV.F result.color.back.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("MOV.F result.color.back.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("MOV.F result.fogcoord.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("MOV.F result.clip[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex) {
|
||||
// RC.x = base_index
|
||||
// RC.y = masked_index
|
||||
// RC.z = compare_index
|
||||
ctx.Add("SHR.S RC.x,{},2;"
|
||||
"AND.S RC.y,RC.x,3;"
|
||||
"SHR.S RC.z,{},4;",
|
||||
offset, offset);
|
||||
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
u32 num_endifs{};
|
||||
const auto read{[&](u32 compare_index, const std::array<std::string, 4>& values) {
|
||||
++num_endifs;
|
||||
ctx.Add("SEQ.S.CC RC.w,RC.z,{};" // compare_index
|
||||
"IF NE.w;"
|
||||
// X
|
||||
"SEQ.S.CC RC.w,RC.y,0;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Y
|
||||
"SEQ.S.CC RC.w,RC.y,1;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Z
|
||||
"SEQ.S.CC RC.w,RC.y,2;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// W
|
||||
"MOV {}.x,{};"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ELSE;",
|
||||
compare_index, ret, values[0], ret, values[1], ret, values[2], ret, values[3]);
|
||||
}};
|
||||
const auto read_swizzled{[&](u32 compare_index, std::string_view value) {
|
||||
const std::array values{fmt::format("{}.x", value), fmt::format("{}.y", value),
|
||||
fmt::format("{}.z", value), fmt::format("{}.w", value)};
|
||||
read(compare_index, values);
|
||||
}};
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const u32 index{static_cast<u32>(IR::Attribute::PositionX)};
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex)));
|
||||
} else {
|
||||
read_swizzled(index, fmt::format("{}.position", ctx.attrib_name));
|
||||
}
|
||||
}
|
||||
for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
|
||||
if (!ctx.info.loads.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex)));
|
||||
}
|
||||
for (u32 i = 0; i < num_endifs; ++i) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarU32 offset,
|
||||
[[maybe_unused]] ScalarF32 value, [[maybe_unused]] ScalarU32 vertex) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
const std::string_view out{ctx.stage == Stage::TessellationControl ? ".out" : ""};
|
||||
ctx.Add("MOV.F {},primitive{}.patch.attrib[{}].{};", inst, out, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("MOV.F result.patch.attrib[{}].{},{};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
ctx.Add("MOV.F result.patch.tessouter[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("MOV.F result.patch.tessinner[0].x,{};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("MOV.F result.patch.tessinner[1].x,{};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value) {
|
||||
ctx.Add("MOV.F frag_color{}.{},{};", index, "xyzw"[component], value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value) {
|
||||
ctx.Add("MOV.S result.samplemask.x,{};", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
|
||||
ctx.Add("MOV.F result.depth.z,{};", value);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.groupid;", inst);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.localid;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
|
||||
ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value) {
|
||||
ctx.Add("MOV.U lmem[{}].x,{};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("KIL TR.x;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("KIL TR.x;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,228 +1,228 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view FpRounding(IR::FpRounding fp_rounding) {
|
||||
switch (fp_rounding) {
|
||||
case IR::FpRounding::DontCare:
|
||||
return "";
|
||||
case IR::FpRounding::RN:
|
||||
return ".ROUND";
|
||||
case IR::FpRounding::RZ:
|
||||
return ".TRUNC";
|
||||
case IR::FpRounding::RM:
|
||||
return ".FLR";
|
||||
case IR::FpRounding::RP:
|
||||
return ".CEIL";
|
||||
}
|
||||
throw InvalidArgument("Invalid floating-point rounding {}", fp_rounding);
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Convert(EmitContext& ctx, IR::Inst& inst, InputType value, std::string_view dest,
|
||||
std::string_view src, bool is_long_result) {
|
||||
const std::string_view fp_rounding{FpRounding(inst.Flags<IR::FpControl>().rounding)};
|
||||
const auto ret{is_long_result ? ctx.reg_alloc.LongDefine(inst) : ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("CVT.{}.{}{} {}.x,{};", dest, src, fp_rounding, ret, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "U64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "F32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F16", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F16", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F32", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F32", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F64", "S32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S64", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U64", true);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view FpRounding(IR::FpRounding fp_rounding) {
|
||||
switch (fp_rounding) {
|
||||
case IR::FpRounding::DontCare:
|
||||
return "";
|
||||
case IR::FpRounding::RN:
|
||||
return ".ROUND";
|
||||
case IR::FpRounding::RZ:
|
||||
return ".TRUNC";
|
||||
case IR::FpRounding::RM:
|
||||
return ".FLR";
|
||||
case IR::FpRounding::RP:
|
||||
return ".CEIL";
|
||||
}
|
||||
throw InvalidArgument("Invalid floating-point rounding {}", fp_rounding);
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Convert(EmitContext& ctx, IR::Inst& inst, InputType value, std::string_view dest,
|
||||
std::string_view src, bool is_long_result) {
|
||||
const std::string_view fp_rounding{FpRounding(inst.Flags<IR::FpControl>().rounding)};
|
||||
const auto ret{is_long_result ? ctx.reg_alloc.LongDefine(inst) : ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("CVT.{}.{}{} {}.x,{};", dest, src, fp_rounding, ret, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "U64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "F32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F16", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F16", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F32", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F32", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F64", "S32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S64", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U64", true);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,411 +1,411 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <typename InputType>
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, InputType lhs, InputType rhs, std::string_view op,
|
||||
std::string_view type, bool ordered, bool inequality = false) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.{} RC.x,{},{};", op, type, lhs, rhs);
|
||||
if (ordered && inequality) {
|
||||
ctx.Add("SEQ.{} RC.y,{},{};"
|
||||
"SEQ.{} RC.z,{},{};"
|
||||
"AND.U RC.x,RC.x,RC.y;"
|
||||
"AND.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
} else if (ordered) {
|
||||
ctx.Add("SNE.S {}.x,RC.x,0;", ret);
|
||||
} else {
|
||||
ctx.Add("SNE.{} RC.y,{},{};"
|
||||
"SNE.{} RC.z,{},{};"
|
||||
"OR.U RC.x,RC.x,RC.y;"
|
||||
"OR.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Clamp(EmitContext& ctx, Register ret, InputType value, InputType min_value,
|
||||
InputType max_value, std::string_view type) {
|
||||
// Call MAX first to properly clamp nan to min_value instead
|
||||
ctx.Add("MAX.{} RC.x,{},{};"
|
||||
"MIN.{} {}.x,RC.x,{};",
|
||||
type, min_value, value, type, ret, max_value);
|
||||
}
|
||||
|
||||
std::string_view Precise(IR::Inst& inst) {
|
||||
const bool precise{inst.Flags<IR::FpControl>().no_contraction};
|
||||
return precise ? ".PREC" : "";
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("ADD.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("ADD.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b,
|
||||
[[maybe_unused]] Register c) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c) {
|
||||
ctx.Add("MAD.F{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c) {
|
||||
ctx.Add("MAD.F64{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MAX.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MAX.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MIN.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MIN.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MUL.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("MUL.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value) {
|
||||
ctx.Add("MOV.F {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("SIN {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("COS {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("EX2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("LG2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RCP {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RSQ {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("RSQ RC.x,{};RCP {}.x,RC.x;", value, ret);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F.SAT {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value,
|
||||
[[maybe_unused]] Register min_value, [[maybe_unused]] Register max_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
|
||||
ScalarF32 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.Define(inst), value, min_value, max_value, "F");
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
|
||||
ScalarF64 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.LongDefine(inst), value, min_value, max_value, "F64");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("ROUND.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("ROUND.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("FLR.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("FLR.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("CEIL.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("CEIL.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("TRUNC.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("TRUNC.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", true, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", true, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", false, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", false, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F", true, false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F64", true, false);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <typename InputType>
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, InputType lhs, InputType rhs, std::string_view op,
|
||||
std::string_view type, bool ordered, bool inequality = false) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.{} RC.x,{},{};", op, type, lhs, rhs);
|
||||
if (ordered && inequality) {
|
||||
ctx.Add("SEQ.{} RC.y,{},{};"
|
||||
"SEQ.{} RC.z,{},{};"
|
||||
"AND.U RC.x,RC.x,RC.y;"
|
||||
"AND.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
} else if (ordered) {
|
||||
ctx.Add("SNE.S {}.x,RC.x,0;", ret);
|
||||
} else {
|
||||
ctx.Add("SNE.{} RC.y,{},{};"
|
||||
"SNE.{} RC.z,{},{};"
|
||||
"OR.U RC.x,RC.x,RC.y;"
|
||||
"OR.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Clamp(EmitContext& ctx, Register ret, InputType value, InputType min_value,
|
||||
InputType max_value, std::string_view type) {
|
||||
// Call MAX first to properly clamp nan to min_value instead
|
||||
ctx.Add("MAX.{} RC.x,{},{};"
|
||||
"MIN.{} {}.x,RC.x,{};",
|
||||
type, min_value, value, type, ret, max_value);
|
||||
}
|
||||
|
||||
std::string_view Precise(IR::Inst& inst) {
|
||||
const bool precise{inst.Flags<IR::FpControl>().no_contraction};
|
||||
return precise ? ".PREC" : "";
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("ADD.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("ADD.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b,
|
||||
[[maybe_unused]] Register c) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c) {
|
||||
ctx.Add("MAD.F{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c) {
|
||||
ctx.Add("MAD.F64{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MAX.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MAX.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MIN.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MIN.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MUL.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("MUL.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value) {
|
||||
ctx.Add("MOV.F {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("SIN {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("COS {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("EX2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("LG2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RCP {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RSQ {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("RSQ RC.x,{};RCP {}.x,RC.x;", value, ret);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F.SAT {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value,
|
||||
[[maybe_unused]] Register min_value, [[maybe_unused]] Register max_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
|
||||
ScalarF32 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.Define(inst), value, min_value, max_value, "F");
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
|
||||
ScalarF64 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.LongDefine(inst), value, min_value, max_value, "F64");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("ROUND.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("ROUND.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("FLR.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("FLR.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("CEIL.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("CEIL.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("TRUNC.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("TRUNC.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", true, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", true, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", false, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", false, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F", true, false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F64", true, false);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,301 +1,301 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b,
|
||||
std::string_view lop) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.S {}.x,{},{};", lop, ret, a, b);
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
const std::array flags{
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp),
|
||||
};
|
||||
for (IR::Inst* const flag_inst : flags) {
|
||||
if (flag_inst) {
|
||||
flag_inst->Invalidate();
|
||||
}
|
||||
}
|
||||
const bool cc{inst.HasAssociatedPseudoOperation()};
|
||||
const std::string_view cc_mod{cc ? ".CC" : ""};
|
||||
if (cc) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("ADD.S{} {}.x,{},{};", cc_mod, ret, a, b);
|
||||
if (!cc) {
|
||||
return;
|
||||
}
|
||||
static constexpr std::array<std::string_view, 4> masks{"", "SF", "CF", "OF"};
|
||||
for (size_t flag_index = 0; flag_index < flags.size(); ++flag_index) {
|
||||
if (!flags[flag_index]) {
|
||||
continue;
|
||||
}
|
||||
const auto flag_ret{ctx.reg_alloc.Define(*flags[flag_index])};
|
||||
if (flag_index == 0) {
|
||||
ctx.Add("SEQ.S {}.x,{}.x,0;", flag_ret, ret);
|
||||
} else {
|
||||
// We could use conditional execution here, but it's broken on Nvidia's compiler
|
||||
ctx.Add("IF {}.x;"
|
||||
"MOV.S {}.x,-1;"
|
||||
"ELSE;"
|
||||
"MOV.S {}.x,0;"
|
||||
"ENDIF;",
|
||||
masks[flag_index], flag_ret, flag_ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("ADD.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("SUB.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("SUB.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MUL.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("DIV.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("DIV.U {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
if (value.type != Type::Register && static_cast<s32>(value.imm_u32) < 0) {
|
||||
ctx.Add("MOV.S {},{};", inst, -static_cast<s32>(value.imm_u32));
|
||||
} else {
|
||||
ctx.Add("MOV.S {},-{};", inst, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.S64 {},-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("ABS.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHL.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHL.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHR.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHR.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift) {
|
||||
ctx.Add("SHR.S {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarS32 shift) {
|
||||
ctx.LongAdd("SHR.S64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "AND");
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "OR");
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "XOR");
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
|
||||
ScalarS32 offset, ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFI.S {},RC,{},{};",
|
||||
count, offset, ret, insert, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
|
||||
ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFE.S {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
|
||||
ScalarU32 count) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.U RC.x,{};"
|
||||
"MOV.U RC.y,{};"
|
||||
"BFE.U {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BFR {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTC {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("NOT.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTFM.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
ctx.Add("BTFM.U {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MIN.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MIN.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MAX.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MAX.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.S RC.x,{},{};"
|
||||
"MAX.S {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.U RC.x,{},{};"
|
||||
"MAX.U {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SEQ.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SNE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b,
|
||||
std::string_view lop) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.S {}.x,{},{};", lop, ret, a, b);
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
const std::array flags{
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp),
|
||||
};
|
||||
for (IR::Inst* const flag_inst : flags) {
|
||||
if (flag_inst) {
|
||||
flag_inst->Invalidate();
|
||||
}
|
||||
}
|
||||
const bool cc{inst.HasAssociatedPseudoOperation()};
|
||||
const std::string_view cc_mod{cc ? ".CC" : ""};
|
||||
if (cc) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("ADD.S{} {}.x,{},{};", cc_mod, ret, a, b);
|
||||
if (!cc) {
|
||||
return;
|
||||
}
|
||||
static constexpr std::array<std::string_view, 4> masks{"", "SF", "CF", "OF"};
|
||||
for (size_t flag_index = 0; flag_index < flags.size(); ++flag_index) {
|
||||
if (!flags[flag_index]) {
|
||||
continue;
|
||||
}
|
||||
const auto flag_ret{ctx.reg_alloc.Define(*flags[flag_index])};
|
||||
if (flag_index == 0) {
|
||||
ctx.Add("SEQ.S {}.x,{}.x,0;", flag_ret, ret);
|
||||
} else {
|
||||
// We could use conditional execution here, but it's broken on Nvidia's compiler
|
||||
ctx.Add("IF {}.x;"
|
||||
"MOV.S {}.x,-1;"
|
||||
"ELSE;"
|
||||
"MOV.S {}.x,0;"
|
||||
"ENDIF;",
|
||||
masks[flag_index], flag_ret, flag_ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("ADD.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("SUB.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("SUB.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MUL.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("DIV.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("DIV.U {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
if (value.type != Type::Register && static_cast<s32>(value.imm_u32) < 0) {
|
||||
ctx.Add("MOV.S {},{};", inst, -static_cast<s32>(value.imm_u32));
|
||||
} else {
|
||||
ctx.Add("MOV.S {},-{};", inst, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.S64 {},-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("ABS.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHL.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHL.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHR.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHR.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift) {
|
||||
ctx.Add("SHR.S {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarS32 shift) {
|
||||
ctx.LongAdd("SHR.S64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "AND");
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "OR");
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "XOR");
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
|
||||
ScalarS32 offset, ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFI.S {},RC,{},{};",
|
||||
count, offset, ret, insert, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
|
||||
ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFE.S {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
|
||||
ScalarU32 count) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.U RC.x,{};"
|
||||
"MOV.U RC.y,{};"
|
||||
"BFE.U {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BFR {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTC {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("NOT.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTFM.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
ctx.Add("BTFM.U {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MIN.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MIN.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MAX.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MAX.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.S RC.x,{},{};"
|
||||
"MAX.S {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.U RC.x,{},{};"
|
||||
"MAX.U {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SEQ.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SNE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("OR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("AND.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("XOR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("SEQ.S {},{},0;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("OR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("AND.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("XOR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("SEQ.S {},{},0;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
|
||||
Register false_value) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
const Register ret{ctx.reg_alloc.LongDefine(inst)};
|
||||
if (ret == true_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(EQ.x),{};",
|
||||
cond, ret, false_value);
|
||||
} else if (ret == false_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, true_value);
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, false_value, ret, true_value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
|
||||
Register false_value) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
const Register ret{ctx.reg_alloc.LongDefine(inst)};
|
||||
if (ret == true_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(EQ.x),{};",
|
||||
cond, ret, false_value);
|
||||
} else if (ret == false_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, true_value);
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, false_value, ret, true_value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X2 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X4 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U8 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U16 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U32 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X2 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X4 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X2 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X4 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U8 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U16 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U32 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X2 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X4 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
switch (phi.Type()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.reg_alloc.Define(phi);
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.reg_alloc.LongDefine(phi);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.reg_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.reg_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
|
||||
const Value eval_value{ctx.reg_alloc.Consume(value)};
|
||||
|
||||
if (phi_reg == eval_value) {
|
||||
return;
|
||||
}
|
||||
switch (phi.Flags<IR::Type>()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext&) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
|
||||
if (stream.type == Type::U32 && stream.imm_u32 == 0) {
|
||||
ctx.Add("EMIT;");
|
||||
} else {
|
||||
ctx.Add("EMITS {};", stream);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (!stream.IsImmediate()) {
|
||||
LOG_WARNING(Shader_GLASM, "Stream is not immediate");
|
||||
}
|
||||
ctx.reg_alloc.Consume(stream);
|
||||
ctx.Add("ENDPRIM;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
switch (phi.Type()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.reg_alloc.Define(phi);
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.reg_alloc.LongDefine(phi);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.reg_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.reg_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
|
||||
const Value eval_value{ctx.reg_alloc.Consume(value)};
|
||||
|
||||
if (phi_reg == eval_value) {
|
||||
return;
|
||||
}
|
||||
switch (phi.Flags<IR::Type>()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext&) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
|
||||
if (stream.type == Type::U32 && stream.imm_u32 == 0) {
|
||||
ctx.Add("EMIT;");
|
||||
} else {
|
||||
ctx.Add("EMITS {};", stream);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (!stream.IsImmediate()) {
|
||||
LOG_WARNING(Shader_GLASM, "Stream is not immediate");
|
||||
}
|
||||
ctx.reg_alloc.Consume(stream);
|
||||
ctx.Add("ENDPRIM;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.LongAdd("MOV.S64 {}.x,0;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.LongAdd("MOV.S64 {}.x,0;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,149 +1,149 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,{}.threadid;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGALL.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGANY.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGEQ.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGBALLOT {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadeqmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadltmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadlemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgtmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
static void Shuffle(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask,
|
||||
std::string_view op) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (in_bounds) {
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
std::string mask;
|
||||
if (clamp.IsImmediate() && segmentation_mask.IsImmediate()) {
|
||||
mask = fmt::to_string(clamp.U32() | (segmentation_mask.U32() << 8));
|
||||
} else {
|
||||
mask = "RC";
|
||||
ctx.Add("BFI.U RC.x,{{5,8,0,0}},{},{};",
|
||||
ScalarU32{ctx.reg_alloc.Consume(segmentation_mask)},
|
||||
ScalarU32{ctx.reg_alloc.Consume(clamp)});
|
||||
}
|
||||
const Register value_ret{ctx.reg_alloc.Define(inst)};
|
||||
if (in_bounds) {
|
||||
const Register bounds_ret{ctx.reg_alloc.Define(*in_bounds)};
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, bounds_ret, value, index, mask, value_ret, bounds_ret);
|
||||
} else {
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, value_ret, value, index, mask, value_ret, value_ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "IDX");
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "UP");
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "DOWN");
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle) {
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("AND.U RC.z,{}.threadid,3;"
|
||||
"SHL.U RC.z,RC.z,1;"
|
||||
"SHR.U RC.z,{},RC.z;"
|
||||
"AND.U RC.z,RC.z,3;"
|
||||
"MUL.F RC.x,{},FSWZA[RC.z];"
|
||||
"MUL.F RC.y,{},FSWZB[RC.z];"
|
||||
"ADD.F {}.x,RC.x,RC.y;",
|
||||
ctx.stage_name, swizzle, op_a, op_b, ret);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,{}.threadid;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGALL.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGANY.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGEQ.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGBALLOT {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadeqmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadltmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadlemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgtmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
static void Shuffle(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask,
|
||||
std::string_view op) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (in_bounds) {
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
std::string mask;
|
||||
if (clamp.IsImmediate() && segmentation_mask.IsImmediate()) {
|
||||
mask = fmt::to_string(clamp.U32() | (segmentation_mask.U32() << 8));
|
||||
} else {
|
||||
mask = "RC";
|
||||
ctx.Add("BFI.U RC.x,{{5,8,0,0}},{},{};",
|
||||
ScalarU32{ctx.reg_alloc.Consume(segmentation_mask)},
|
||||
ScalarU32{ctx.reg_alloc.Consume(clamp)});
|
||||
}
|
||||
const Register value_ret{ctx.reg_alloc.Define(inst)};
|
||||
if (in_bounds) {
|
||||
const Register bounds_ret{ctx.reg_alloc.Define(*in_bounds)};
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, bounds_ret, value, index, mask, value_ret, bounds_ret);
|
||||
} else {
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, value_ret, value, index, mask, value_ret, value_ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "IDX");
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "UP");
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "DOWN");
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle) {
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("AND.U RC.z,{}.threadid,3;"
|
||||
"SHL.U RC.z,RC.z,1;"
|
||||
"SHR.U RC.z,{},RC.z;"
|
||||
"AND.U RC.z,RC.z,3;"
|
||||
"MUL.F RC.x,{},FSWZA[RC.z];"
|
||||
"MUL.F RC.y,{},FSWZB[RC.z];"
|
||||
"ADD.F {}.x,RC.x,RC.y;",
|
||||
ctx.stage_name, swizzle, op_a, op_b, ret);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view InterpDecorator(Interpolation interp) {
|
||||
switch (interp) {
|
||||
case Interpolation::Smooth:
|
||||
return "";
|
||||
case Interpolation::Flat:
|
||||
return "FLAT ";
|
||||
case Interpolation::NoPerspective:
|
||||
return "NOPERSPECTIVE ";
|
||||
}
|
||||
throw InvalidArgument("Invalid interpolation {}", interp);
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_)
|
||||
: info{program.info}, profile{profile_}, runtime_info{runtime_info_} {
|
||||
// FIXME: Temporary partial implementation
|
||||
u32 cbuf_index{};
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Constant buffer descriptor array");
|
||||
}
|
||||
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
|
||||
++cbuf_index;
|
||||
}
|
||||
u32 ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Storage buffer descriptor array");
|
||||
}
|
||||
if (runtime_info.glasm_use_storage_buffers) {
|
||||
Add("STORAGE ssbo{}[]={{program.storage[{}]}};", ssbo_index, bindings.storage_buffer);
|
||||
++bindings.storage_buffer;
|
||||
++ssbo_index;
|
||||
}
|
||||
}
|
||||
if (!runtime_info.glasm_use_storage_buffers) {
|
||||
if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
|
||||
const size_t index{num + PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE};
|
||||
Add("PARAM c[{}]={{program.local[0..{}]}};", index, index - 1);
|
||||
}
|
||||
}
|
||||
stage = program.stage;
|
||||
switch (program.stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
stage_name = "vertex";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "primitive";
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::Fragment:
|
||||
stage_name = "fragment";
|
||||
attrib_name = "fragment";
|
||||
break;
|
||||
case Stage::Compute:
|
||||
stage_name = "invocation";
|
||||
break;
|
||||
}
|
||||
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (loads.Generic(index)) {
|
||||
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
||||
InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
|
||||
}
|
||||
}
|
||||
if (IsInputArray(stage) && loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
Add("ATTRIB vertex_position=vertex.position;");
|
||||
}
|
||||
if (info.uses_invocation_id) {
|
||||
Add("ATTRIB primitive_invocation=primitive.invocation;");
|
||||
}
|
||||
if (info.stores_tess_level_outer) {
|
||||
Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
|
||||
}
|
||||
if (info.stores_tess_level_inner) {
|
||||
Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};");
|
||||
}
|
||||
if (info.stores.ClipDistances()) {
|
||||
Add("OUTPUT result_clip[]={{result.clip[0..7]}};");
|
||||
}
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
continue;
|
||||
}
|
||||
if (stage == Stage::TessellationControl) {
|
||||
Add("OUTPUT result_patch_attrib{}[]={{result.patch.attrib[{}..{}]}};"
|
||||
"ATTRIB primitive_out_patch_attrib{}[]={{primitive.out.patch.attrib[{}..{}]}};",
|
||||
index, index, index, index, index, index);
|
||||
} else {
|
||||
Add("ATTRIB primitive_patch_attrib{}[]={{primitive.patch.attrib[{}..{}]}};", index,
|
||||
index, index);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
Add("OUTPUT frag_color0=result.color;");
|
||||
for (size_t index = 1; index < info.stores_frag_color.size(); ++index) {
|
||||
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
||||
}
|
||||
}
|
||||
image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
image_buffer_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
image_bindings.reserve(info.image_descriptors.size());
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
image_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
texture_buffer_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
texture_bindings.reserve(info.texture_descriptors.size());
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
texture_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view InterpDecorator(Interpolation interp) {
|
||||
switch (interp) {
|
||||
case Interpolation::Smooth:
|
||||
return "";
|
||||
case Interpolation::Flat:
|
||||
return "FLAT ";
|
||||
case Interpolation::NoPerspective:
|
||||
return "NOPERSPECTIVE ";
|
||||
}
|
||||
throw InvalidArgument("Invalid interpolation {}", interp);
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_)
|
||||
: info{program.info}, profile{profile_}, runtime_info{runtime_info_} {
|
||||
// FIXME: Temporary partial implementation
|
||||
u32 cbuf_index{};
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Constant buffer descriptor array");
|
||||
}
|
||||
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
|
||||
++cbuf_index;
|
||||
}
|
||||
u32 ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Storage buffer descriptor array");
|
||||
}
|
||||
if (runtime_info.glasm_use_storage_buffers) {
|
||||
Add("STORAGE ssbo{}[]={{program.storage[{}]}};", ssbo_index, bindings.storage_buffer);
|
||||
++bindings.storage_buffer;
|
||||
++ssbo_index;
|
||||
}
|
||||
}
|
||||
if (!runtime_info.glasm_use_storage_buffers) {
|
||||
if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
|
||||
const size_t index{num + PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE};
|
||||
Add("PARAM c[{}]={{program.local[0..{}]}};", index, index - 1);
|
||||
}
|
||||
}
|
||||
stage = program.stage;
|
||||
switch (program.stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
stage_name = "vertex";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "primitive";
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::Fragment:
|
||||
stage_name = "fragment";
|
||||
attrib_name = "fragment";
|
||||
break;
|
||||
case Stage::Compute:
|
||||
stage_name = "invocation";
|
||||
break;
|
||||
}
|
||||
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (loads.Generic(index)) {
|
||||
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
||||
InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
|
||||
}
|
||||
}
|
||||
if (IsInputArray(stage) && loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
Add("ATTRIB vertex_position=vertex.position;");
|
||||
}
|
||||
if (info.uses_invocation_id) {
|
||||
Add("ATTRIB primitive_invocation=primitive.invocation;");
|
||||
}
|
||||
if (info.stores_tess_level_outer) {
|
||||
Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
|
||||
}
|
||||
if (info.stores_tess_level_inner) {
|
||||
Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};");
|
||||
}
|
||||
if (info.stores.ClipDistances()) {
|
||||
Add("OUTPUT result_clip[]={{result.clip[0..7]}};");
|
||||
}
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
continue;
|
||||
}
|
||||
if (stage == Stage::TessellationControl) {
|
||||
Add("OUTPUT result_patch_attrib{}[]={{result.patch.attrib[{}..{}]}};"
|
||||
"ATTRIB primitive_out_patch_attrib{}[]={{primitive.out.patch.attrib[{}..{}]}};",
|
||||
index, index, index, index, index, index);
|
||||
} else {
|
||||
Add("ATTRIB primitive_patch_attrib{}[]={{primitive.patch.attrib[{}..{}]}};", index,
|
||||
index, index);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
Add("OUTPUT frag_color0=result.color;");
|
||||
for (size_t index = 1; index < info.stores_frag_color.size(); ++index) {
|
||||
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
||||
}
|
||||
}
|
||||
image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
image_buffer_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
image_bindings.reserve(info.image_descriptors.size());
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
image_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
texture_buffer_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
texture_bindings.reserve(info.texture_descriptors.size());
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
texture_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.Define(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void LongAdd(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.LongDefine(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string code;
|
||||
RegAlloc reg_alloc{};
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
std::vector<u32> texture_buffer_bindings;
|
||||
std::vector<u32> image_buffer_bindings;
|
||||
std::vector<u32> texture_bindings;
|
||||
std::vector<u32> image_bindings;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view attrib_name = "invalid";
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
bool uses_y_direction{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.Define(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void LongAdd(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.LongDefine(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string code;
|
||||
RegAlloc reg_alloc{};
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
std::vector<u32> texture_buffer_bindings;
|
||||
std::vector<u32> image_buffer_bindings;
|
||||
std::vector<u32> texture_bindings;
|
||||
std::vector<u32> image_bindings;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view attrib_name = "invalid";
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
bool uses_y_direction{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,182 +1,182 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst) {
|
||||
return Define(inst, false);
|
||||
}
|
||||
|
||||
Register RegAlloc::LongDefine(IR::Inst& inst) {
|
||||
return Define(inst, true);
|
||||
}
|
||||
|
||||
Value RegAlloc::Peek(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return PeekInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
Value RegAlloc::Consume(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return ConsumeInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::Unref(IR::Inst& inst) {
|
||||
IR::Inst& value_inst{AliasInst(inst)};
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
if (!value_inst.HasUses()) {
|
||||
Free(value_inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocLongReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RegAlloc::FreeReg(Register reg) {
|
||||
Free(reg.id);
|
||||
}
|
||||
|
||||
Value RegAlloc::MakeImm(const IR::Value& value) {
|
||||
Value ret;
|
||||
switch (value.Type()) {
|
||||
case IR::Type::Void:
|
||||
ret.type = Type::Void;
|
||||
break;
|
||||
case IR::Type::U1:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U1() ? 0xffffffff : 0;
|
||||
break;
|
||||
case IR::Type::U32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U32();
|
||||
break;
|
||||
case IR::Type::F32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = Common::BitCast<u32>(value.F32());
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = value.U64();
|
||||
break;
|
||||
case IR::Type::F64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = Common::BitCast<u64>(value.F64());
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst, bool is_long) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(is_long));
|
||||
} else {
|
||||
Id id{};
|
||||
id.is_long.Assign(is_long ? 1 : 0);
|
||||
id.is_null.Assign(1);
|
||||
inst.SetDefinition<Id>(id);
|
||||
}
|
||||
return Register{PeekInst(inst)};
|
||||
}
|
||||
|
||||
Value RegAlloc::PeekInst(IR::Inst& inst) {
|
||||
Value ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = inst.Definition<Id>();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Value RegAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
Unref(inst);
|
||||
return PeekInst(inst);
|
||||
}
|
||||
|
||||
Id RegAlloc::Alloc(bool is_long) {
|
||||
size_t& num_regs{is_long ? num_used_long_registers : num_used_registers};
|
||||
std::bitset<NUM_REGS>& use{is_long ? long_register_use : register_use};
|
||||
if (num_used_registers + num_used_long_registers < NUM_REGS) {
|
||||
for (size_t reg = 0; reg < NUM_REGS; ++reg) {
|
||||
if (use[reg]) {
|
||||
continue;
|
||||
}
|
||||
num_regs = std::max(num_regs, reg + 1);
|
||||
use[reg] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.is_long.Assign(is_long ? 1 : 0);
|
||||
ret.is_spill.Assign(0);
|
||||
ret.is_condition_code.Assign(0);
|
||||
ret.is_null.Assign(0);
|
||||
ret.index.Assign(static_cast<u32>(reg));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
throw NotImplementedException("Register spilling");
|
||||
}
|
||||
|
||||
void RegAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid register");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Free spill");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
long_register_use[id.index] = false;
|
||||
} else {
|
||||
register_use[id.index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool RegAlloc::IsAliased(const IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::Identity:
|
||||
case IR::Opcode::BitCastU16F16:
|
||||
case IR::Opcode::BitCastU32F32:
|
||||
case IR::Opcode::BitCastU64F64:
|
||||
case IR::Opcode::BitCastF16U16:
|
||||
case IR::Opcode::BitCastF32U32:
|
||||
case IR::Opcode::BitCastF64U64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ IR::Inst& RegAlloc::AliasInst(IR::Inst& inst) {
|
||||
IR::Inst* it{&inst};
|
||||
while (IsAliased(*it)) {
|
||||
const IR::Value arg{it->Arg(0)};
|
||||
if (arg.IsImmediate()) {
|
||||
break;
|
||||
}
|
||||
it = arg.InstRecursive();
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst) {
|
||||
return Define(inst, false);
|
||||
}
|
||||
|
||||
Register RegAlloc::LongDefine(IR::Inst& inst) {
|
||||
return Define(inst, true);
|
||||
}
|
||||
|
||||
Value RegAlloc::Peek(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return PeekInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
Value RegAlloc::Consume(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return ConsumeInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::Unref(IR::Inst& inst) {
|
||||
IR::Inst& value_inst{AliasInst(inst)};
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
if (!value_inst.HasUses()) {
|
||||
Free(value_inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocLongReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RegAlloc::FreeReg(Register reg) {
|
||||
Free(reg.id);
|
||||
}
|
||||
|
||||
Value RegAlloc::MakeImm(const IR::Value& value) {
|
||||
Value ret;
|
||||
switch (value.Type()) {
|
||||
case IR::Type::Void:
|
||||
ret.type = Type::Void;
|
||||
break;
|
||||
case IR::Type::U1:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U1() ? 0xffffffff : 0;
|
||||
break;
|
||||
case IR::Type::U32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U32();
|
||||
break;
|
||||
case IR::Type::F32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = Common::BitCast<u32>(value.F32());
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = value.U64();
|
||||
break;
|
||||
case IR::Type::F64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = Common::BitCast<u64>(value.F64());
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst, bool is_long) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(is_long));
|
||||
} else {
|
||||
Id id{};
|
||||
id.is_long.Assign(is_long ? 1 : 0);
|
||||
id.is_null.Assign(1);
|
||||
inst.SetDefinition<Id>(id);
|
||||
}
|
||||
return Register{PeekInst(inst)};
|
||||
}
|
||||
|
||||
Value RegAlloc::PeekInst(IR::Inst& inst) {
|
||||
Value ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = inst.Definition<Id>();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Value RegAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
Unref(inst);
|
||||
return PeekInst(inst);
|
||||
}
|
||||
|
||||
Id RegAlloc::Alloc(bool is_long) {
|
||||
size_t& num_regs{is_long ? num_used_long_registers : num_used_registers};
|
||||
std::bitset<NUM_REGS>& use{is_long ? long_register_use : register_use};
|
||||
if (num_used_registers + num_used_long_registers < NUM_REGS) {
|
||||
for (size_t reg = 0; reg < NUM_REGS; ++reg) {
|
||||
if (use[reg]) {
|
||||
continue;
|
||||
}
|
||||
num_regs = std::max(num_regs, reg + 1);
|
||||
use[reg] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.is_long.Assign(is_long ? 1 : 0);
|
||||
ret.is_spill.Assign(0);
|
||||
ret.is_condition_code.Assign(0);
|
||||
ret.is_null.Assign(0);
|
||||
ret.index.Assign(static_cast<u32>(reg));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
throw NotImplementedException("Register spilling");
|
||||
}
|
||||
|
||||
void RegAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid register");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Free spill");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
long_register_use[id.index] = false;
|
||||
} else {
|
||||
register_use[id.index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool RegAlloc::IsAliased(const IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::Identity:
|
||||
case IR::Opcode::BitCastU16F16:
|
||||
case IR::Opcode::BitCastU32F32:
|
||||
case IR::Opcode::BitCastU64F64:
|
||||
case IR::Opcode::BitCastF16U16:
|
||||
case IR::Opcode::BitCastF32U32:
|
||||
case IR::Opcode::BitCastF64U64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ IR::Inst& RegAlloc::AliasInst(IR::Inst& inst) {
|
||||
IR::Inst* it{&inst};
|
||||
while (IsAliased(*it)) {
|
||||
const IR::Value arg{it->Arg(0)};
|
||||
if (arg.IsImmediate()) {
|
||||
break;
|
||||
}
|
||||
it = arg.InstRecursive();
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
@@ -1,302 +1,302 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext;
|
||||
|
||||
enum class Type : u32 {
|
||||
Void,
|
||||
Register,
|
||||
U32,
|
||||
U64,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 1, u32> is_long;
|
||||
BitField<2, 1, u32> is_spill;
|
||||
BitField<3, 1, u32> is_condition_code;
|
||||
BitField<4, 1, u32> is_null;
|
||||
BitField<5, 27, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
struct Value {
|
||||
Type type;
|
||||
union {
|
||||
Id id;
|
||||
u32 imm_u32;
|
||||
u64 imm_u64;
|
||||
};
|
||||
|
||||
bool operator==(const Value& rhs) const noexcept {
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
case Type::Void:
|
||||
return true;
|
||||
case Type::Register:
|
||||
return id == rhs.id;
|
||||
case Type::U32:
|
||||
return imm_u32 == rhs.imm_u32;
|
||||
case Type::U64:
|
||||
return imm_u64 == rhs.imm_u64;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator!=(const Value& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
struct Register : Value {};
|
||||
struct ScalarRegister : Value {};
|
||||
struct ScalarU32 : Value {};
|
||||
struct ScalarS32 : Value {};
|
||||
struct ScalarF32 : Value {};
|
||||
struct ScalarF64 : Value {};
|
||||
|
||||
class RegAlloc {
|
||||
public:
|
||||
RegAlloc() = default;
|
||||
|
||||
Register Define(IR::Inst& inst);
|
||||
|
||||
Register LongDefine(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Value Peek(const IR::Value& value);
|
||||
|
||||
Value Consume(const IR::Value& value);
|
||||
|
||||
void Unref(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Register AllocReg();
|
||||
|
||||
[[nodiscard]] Register AllocLongReg();
|
||||
|
||||
void FreeReg(Register reg);
|
||||
|
||||
void InvalidateConditionCodes() {
|
||||
// This does nothing for now
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedRegisters() const noexcept {
|
||||
return num_used_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedLongRegisters() const noexcept {
|
||||
return num_used_long_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEmpty() const noexcept {
|
||||
return register_use.none() && long_register_use.none();
|
||||
}
|
||||
|
||||
/// Returns true if the instruction is expected to be aliased to another
|
||||
static bool IsAliased(const IR::Inst& inst);
|
||||
|
||||
/// Returns the underlying value out of an alias sequence
|
||||
static IR::Inst& AliasInst(IR::Inst& inst);
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_REGS = 4096;
|
||||
static constexpr size_t NUM_ELEMENTS = 4;
|
||||
|
||||
Value MakeImm(const IR::Value& value);
|
||||
|
||||
Register Define(IR::Inst& inst, bool is_long);
|
||||
|
||||
Value PeekInst(IR::Inst& inst);
|
||||
|
||||
Value ConsumeInst(IR::Inst& inst);
|
||||
|
||||
Id Alloc(bool is_long);
|
||||
|
||||
void Free(Id id);
|
||||
|
||||
size_t num_used_registers{};
|
||||
size_t num_used_long_registers{};
|
||||
std::bitset<NUM_REGS> register_use{};
|
||||
std::bitset<NUM_REGS> long_register_use{};
|
||||
};
|
||||
|
||||
template <bool scalar, typename FormatContext>
|
||||
auto FormatTo(FormatContext& ctx, Id id) {
|
||||
if (id.is_condition_code != 0) {
|
||||
throw NotImplementedException("Condition code emission");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Spill emission");
|
||||
}
|
||||
if constexpr (scalar) {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC.x" : "RC.x");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}.x", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}.x", id.index.Value());
|
||||
}
|
||||
} else {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC" : "RC");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}", id.index.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Id> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(Shader::Backend::GLASM::Id id, FormatContext& ctx) {
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Register> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::Register& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<false>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarRegister> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarRegister& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarU32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarU32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", value.imm_u32);
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarS32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarS32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", static_cast<s32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF64> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF64& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f64>(value.imm_u64));
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext;
|
||||
|
||||
enum class Type : u32 {
|
||||
Void,
|
||||
Register,
|
||||
U32,
|
||||
U64,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 1, u32> is_long;
|
||||
BitField<2, 1, u32> is_spill;
|
||||
BitField<3, 1, u32> is_condition_code;
|
||||
BitField<4, 1, u32> is_null;
|
||||
BitField<5, 27, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
struct Value {
|
||||
Type type;
|
||||
union {
|
||||
Id id;
|
||||
u32 imm_u32;
|
||||
u64 imm_u64;
|
||||
};
|
||||
|
||||
bool operator==(const Value& rhs) const noexcept {
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
case Type::Void:
|
||||
return true;
|
||||
case Type::Register:
|
||||
return id == rhs.id;
|
||||
case Type::U32:
|
||||
return imm_u32 == rhs.imm_u32;
|
||||
case Type::U64:
|
||||
return imm_u64 == rhs.imm_u64;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator!=(const Value& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
struct Register : Value {};
|
||||
struct ScalarRegister : Value {};
|
||||
struct ScalarU32 : Value {};
|
||||
struct ScalarS32 : Value {};
|
||||
struct ScalarF32 : Value {};
|
||||
struct ScalarF64 : Value {};
|
||||
|
||||
class RegAlloc {
|
||||
public:
|
||||
RegAlloc() = default;
|
||||
|
||||
Register Define(IR::Inst& inst);
|
||||
|
||||
Register LongDefine(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Value Peek(const IR::Value& value);
|
||||
|
||||
Value Consume(const IR::Value& value);
|
||||
|
||||
void Unref(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Register AllocReg();
|
||||
|
||||
[[nodiscard]] Register AllocLongReg();
|
||||
|
||||
void FreeReg(Register reg);
|
||||
|
||||
void InvalidateConditionCodes() {
|
||||
// This does nothing for now
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedRegisters() const noexcept {
|
||||
return num_used_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedLongRegisters() const noexcept {
|
||||
return num_used_long_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEmpty() const noexcept {
|
||||
return register_use.none() && long_register_use.none();
|
||||
}
|
||||
|
||||
/// Returns true if the instruction is expected to be aliased to another
|
||||
static bool IsAliased(const IR::Inst& inst);
|
||||
|
||||
/// Returns the underlying value out of an alias sequence
|
||||
static IR::Inst& AliasInst(IR::Inst& inst);
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_REGS = 4096;
|
||||
static constexpr size_t NUM_ELEMENTS = 4;
|
||||
|
||||
Value MakeImm(const IR::Value& value);
|
||||
|
||||
Register Define(IR::Inst& inst, bool is_long);
|
||||
|
||||
Value PeekInst(IR::Inst& inst);
|
||||
|
||||
Value ConsumeInst(IR::Inst& inst);
|
||||
|
||||
Id Alloc(bool is_long);
|
||||
|
||||
void Free(Id id);
|
||||
|
||||
size_t num_used_registers{};
|
||||
size_t num_used_long_registers{};
|
||||
std::bitset<NUM_REGS> register_use{};
|
||||
std::bitset<NUM_REGS> long_register_use{};
|
||||
};
|
||||
|
||||
template <bool scalar, typename FormatContext>
|
||||
auto FormatTo(FormatContext& ctx, Id id) {
|
||||
if (id.is_condition_code != 0) {
|
||||
throw NotImplementedException("Condition code emission");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Spill emission");
|
||||
}
|
||||
if constexpr (scalar) {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC.x" : "RC.x");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}.x", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}.x", id.index.Value());
|
||||
}
|
||||
} else {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC" : "RC");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}", id.index.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Id> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(Shader::Backend::GLASM::Id id, FormatContext& ctx) {
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Register> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::Register& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<false>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarRegister> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarRegister& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarU32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarU32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", value.imm_u32);
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarS32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarS32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", static_cast<s32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF64> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF64& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f64>(value.imm_u64));
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,251 +1,251 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||
return ctx.var_alloc.Consume(arg);
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return arg.U32();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return arg.Attribute();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return arg.Patch();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return arg.Reg();
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, *inst,
|
||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("}}");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("break;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("return;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("for(;;){{");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (Settings::values.disable_shader_loop_safety_checks) {
|
||||
ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
} else {
|
||||
ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
|
||||
ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPreciseType(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::PrecF32:
|
||||
case GlslVarType::PrecF64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefineVariables(const EmitContext& ctx, std::string& header) {
|
||||
for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
|
||||
const auto type{static_cast<GlslVarType>(i)};
|
||||
const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
|
||||
const auto type_name{ctx.var_alloc.GetGlslType(type)};
|
||||
const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
|
||||
const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
|
||||
// Temps/return types that are never used are stored at index 0
|
||||
if (tracker.uses_temp) {
|
||||
header += fmt::format("{}{} t{}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(0, type), type_name);
|
||||
}
|
||||
for (u32 index = 0; index < tracker.num_used; ++index) {
|
||||
header += fmt::format("{}{} {}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(index, type), type_name);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
|
||||
header += fmt::format("int loop{}=0x2000;", i);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
const auto max_size{profile.gl_max_compute_smem_size};
|
||||
const bool needs_clamp{requested_size > max_size};
|
||||
if (needs_clamp) {
|
||||
LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
|
||||
requested_size, max_size);
|
||||
}
|
||||
const auto smem_size{needs_clamp ? max_size : requested_size};
|
||||
ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
|
||||
}
|
||||
ctx.header += "void main(){\n";
|
||||
if (program.local_memory_size > 0) {
|
||||
ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
|
||||
}
|
||||
DefineVariables(ctx, ctx.header);
|
||||
if (ctx.uses_cc_carry) {
|
||||
ctx.header += "uint carry;";
|
||||
}
|
||||
if (program.info.uses_subgroup_shuffles) {
|
||||
ctx.header += "bool shfl_in_bounds;";
|
||||
}
|
||||
ctx.code.insert(0, ctx.header);
|
||||
ctx.code += '}';
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||
return ctx.var_alloc.Consume(arg);
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return arg.U32();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return arg.Attribute();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return arg.Patch();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return arg.Reg();
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, *inst,
|
||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("}}");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("break;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("return;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("for(;;){{");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (Settings::values.disable_shader_loop_safety_checks) {
|
||||
ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
} else {
|
||||
ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
|
||||
ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPreciseType(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::PrecF32:
|
||||
case GlslVarType::PrecF64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefineVariables(const EmitContext& ctx, std::string& header) {
|
||||
for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
|
||||
const auto type{static_cast<GlslVarType>(i)};
|
||||
const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
|
||||
const auto type_name{ctx.var_alloc.GetGlslType(type)};
|
||||
const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
|
||||
const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
|
||||
// Temps/return types that are never used are stored at index 0
|
||||
if (tracker.uses_temp) {
|
||||
header += fmt::format("{}{} t{}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(0, type), type_name);
|
||||
}
|
||||
for (u32 index = 0; index < tracker.num_used; ++index) {
|
||||
header += fmt::format("{}{} {}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(index, type), type_name);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
|
||||
header += fmt::format("int loop{}=0x2000;", i);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
const auto max_size{profile.gl_max_compute_smem_size};
|
||||
const bool needs_clamp{requested_size > max_size};
|
||||
if (needs_clamp) {
|
||||
LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
|
||||
requested_size, max_size);
|
||||
}
|
||||
const auto smem_size{needs_clamp ? max_size : requested_size};
|
||||
ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
|
||||
}
|
||||
ctx.header += "void main(){\n";
|
||||
if (program.local_memory_size > 0) {
|
||||
ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
|
||||
}
|
||||
DefineVariables(ctx, ctx.header);
|
||||
if (ctx.uses_cc_carry) {
|
||||
ctx.header += "uint carry;";
|
||||
}
|
||||
if (program.info.uses_subgroup_shuffles) {
|
||||
ctx.header += "bool shfl_in_bounds;";
|
||||
}
|
||||
ctx.code.insert(0, ctx.header);
|
||||
ctx.code += '}';
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLSL(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLSL(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("barrier();");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("groupMemoryBarrier();");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("memoryBarrier();");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("barrier();");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("groupMemoryBarrier();");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("memoryBarrier();");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{*value.InstRecursive()};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real variable out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=ftou({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=utof({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=packDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{*value.InstRecursive()};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real variable out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=ftou({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=utof({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=packDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,218 +1,218 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr std::string_view SWIZZLE{"xyzw"};
|
||||
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
if (result == composite) {
|
||||
// The result is aliased with the composite
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
} else {
|
||||
ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3,
|
||||
[[maybe_unused]] std::string_view e4) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr std::string_view SWIZZLE{"xyzw"};
|
||||
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
if (result == composite) {
|
||||
// The result is aliased with the composite
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
} else {
|
||||
ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3,
|
||||
[[maybe_unused]] std::string_view e4) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,427 +1,427 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char SWIZZLE[]{"xyzw"};
|
||||
|
||||
u32 CbufIndex(u32 offset) {
|
||||
return (offset / 4) % 4;
|
||||
}
|
||||
|
||||
char OffsetSwizzle(u32 offset) {
|
||||
return SWIZZLE[CbufIndex(offset)];
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
|
||||
if (binding.IsImmediate()) {
|
||||
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
|
||||
} else {
|
||||
const auto binding_var{ctx.var_alloc.Consume(binding)};
|
||||
return fmt::format("GetCbufIndirect({},{})", binding_var, index);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
|
||||
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
|
||||
std::string_view bit_offset = {}) {
|
||||
const bool is_immediate{offset.IsImmediate()};
|
||||
const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
|
||||
if (is_immediate) {
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
if (signed_offset < 0 || offset.U32() > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.Add("{}=0u;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
|
||||
: fmt::format("{}>>4", offset_var)};
|
||||
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
|
||||
: fmt::format("[({}>>2)%4]", offset_var)};
|
||||
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, index)};
|
||||
const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
|
||||
const auto extraction{num_bits == 32 ? cbuf_cast
|
||||
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
|
||||
bit_offset, num_bits)};
|
||||
if (!component_indexing_bug) {
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
|
||||
ctx.Add("{}={};", ret, result);
|
||||
return;
|
||||
}
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
|
||||
ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
if (signed_offset < 0 || u32_offset > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.AddU32x2("{}=uvec2(0u);", inst);
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
|
||||
offset_var, cast, cbuf, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
|
||||
cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
|
||||
if (element == 3) {
|
||||
ctx.AddF32("{}=1.f;", inst, attr);
|
||||
} else {
|
||||
ctx.AddF32("{}=0.f;", inst, attr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW: {
|
||||
const bool is_array{IsInputArray(ctx.stage)};
|
||||
const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddF32("{}=itof(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddF32("{}=itof(gl_VertexID);", inst);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const u32 attr_element{IR::GenericAttributeElement(attr)};
|
||||
const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
if (info.num_components == 1) {
|
||||
ctx.Add("{}{}={};", info.name, output_decorator, value);
|
||||
} else {
|
||||
const u32 index_element{attr_element - info.first_element};
|
||||
ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_Layer=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportIndex=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
|
||||
LOG_WARNING(
|
||||
Shader_GLSL,
|
||||
"Shader stores viewport mask but device does not support viewport mask extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("gl_PointSize={};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("gl_ClipDistance[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
std::string_view vertex) {
|
||||
const bool is_array{ctx.stage == Stage::Geometry};
|
||||
const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
|
||||
ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view offset,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("gl_TessLevelInner[0]={};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("gl_TessLevelInner[1]={};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
|
||||
const char swizzle{"xyzw"[component]};
|
||||
ctx.Add("frag_color{}.{}={};", index, swizzle, value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_SampleMask[0]=int({});", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_FragDepth={};", value);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_InvocationID);", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_SampleID);", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=gl_HelperInvocation;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddF32("{}=scaling.z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
|
||||
ctx.AddU32("{}=lmem[{}];", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
|
||||
ctx.Add("lmem[{}]={};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char SWIZZLE[]{"xyzw"};
|
||||
|
||||
u32 CbufIndex(u32 offset) {
|
||||
return (offset / 4) % 4;
|
||||
}
|
||||
|
||||
char OffsetSwizzle(u32 offset) {
|
||||
return SWIZZLE[CbufIndex(offset)];
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
|
||||
if (binding.IsImmediate()) {
|
||||
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
|
||||
} else {
|
||||
const auto binding_var{ctx.var_alloc.Consume(binding)};
|
||||
return fmt::format("GetCbufIndirect({},{})", binding_var, index);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
|
||||
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
|
||||
std::string_view bit_offset = {}) {
|
||||
const bool is_immediate{offset.IsImmediate()};
|
||||
const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
|
||||
if (is_immediate) {
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
if (signed_offset < 0 || offset.U32() > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.Add("{}=0u;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
|
||||
: fmt::format("{}>>4", offset_var)};
|
||||
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
|
||||
: fmt::format("[({}>>2)%4]", offset_var)};
|
||||
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, index)};
|
||||
const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
|
||||
const auto extraction{num_bits == 32 ? cbuf_cast
|
||||
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
|
||||
bit_offset, num_bits)};
|
||||
if (!component_indexing_bug) {
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
|
||||
ctx.Add("{}={};", ret, result);
|
||||
return;
|
||||
}
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
|
||||
ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
if (signed_offset < 0 || u32_offset > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.AddU32x2("{}=uvec2(0u);", inst);
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
|
||||
offset_var, cast, cbuf, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
|
||||
cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
|
||||
if (element == 3) {
|
||||
ctx.AddF32("{}=1.f;", inst, attr);
|
||||
} else {
|
||||
ctx.AddF32("{}=0.f;", inst, attr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW: {
|
||||
const bool is_array{IsInputArray(ctx.stage)};
|
||||
const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddF32("{}=itof(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddF32("{}=itof(gl_VertexID);", inst);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const u32 attr_element{IR::GenericAttributeElement(attr)};
|
||||
const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
if (info.num_components == 1) {
|
||||
ctx.Add("{}{}={};", info.name, output_decorator, value);
|
||||
} else {
|
||||
const u32 index_element{attr_element - info.first_element};
|
||||
ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_Layer=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportIndex=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
|
||||
LOG_WARNING(
|
||||
Shader_GLSL,
|
||||
"Shader stores viewport mask but device does not support viewport mask extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("gl_PointSize={};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("gl_ClipDistance[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
std::string_view vertex) {
|
||||
const bool is_array{ctx.stage == Stage::Geometry};
|
||||
const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
|
||||
ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view offset,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("gl_TessLevelInner[0]={};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("gl_TessLevelInner[1]={};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
|
||||
const char swizzle{"xyzw"[component]};
|
||||
ctx.Add("frag_color{}.{}={};", index, swizzle, value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_SampleMask[0]=int({});", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_FragDepth={};", value);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_InvocationID);", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_SampleID);", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=gl_HelperInvocation;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddF32("{}=scaling.z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
|
||||
ctx.AddU32("{}=lmem[{}];", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
|
||||
ctx.Add("lmem[{}]={};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("discard;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("discard;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,229 +1,229 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({}&0xffff);", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({}&0xffff);", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,455 +1,455 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
|
||||
std::string_view op, bool ordered) {
|
||||
const auto nan_op{ordered ? "&&!" : "||"};
|
||||
ctx.AddU1("{}={}{}{}"
|
||||
"{}isnan({}){}isnan({});",
|
||||
inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
|
||||
}
|
||||
|
||||
bool IsPrecise(const IR::Inst& inst) {
|
||||
return inst.Flags<IR::FpControl>().no_contraction;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
|
||||
[[maybe_unused]] std::string_view c) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sin({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=cos({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=exp2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=log2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=(1.0f)/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=1.0/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
ctx.AddF32("{}=inversesqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view min_value,
|
||||
[[maybe_unused]] std::string_view max_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
|
||||
std::string_view op, bool ordered) {
|
||||
const auto nan_op{ordered ? "&&!" : "||"};
|
||||
ctx.AddU1("{}={}{}{}"
|
||||
"{}isnan({}){}isnan({});",
|
||||
inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
|
||||
}
|
||||
|
||||
bool IsPrecise(const IR::Inst& inst) {
|
||||
return inst.Flags<IR::FpControl>().no_contraction;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
|
||||
[[maybe_unused]] std::string_view c) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sin({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=cos({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=exp2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=log2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=(1.0f)/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=1.0/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
ctx.AddF32("{}=inversesqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view min_value,
|
||||
[[maybe_unused]] std::string_view max_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,258 +1,258 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}={}==0;", *zero, result);
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=int({})<0;", *sign, result);
|
||||
sign->Invalidate();
|
||||
}
|
||||
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
char lop) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}={}{}{};", result, a, lop, b);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
// Compute the overflow CC first as it requires the original operand values,
|
||||
// which may be overwritten by the result of the addition
|
||||
if (IR::Inst * overflow{inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
|
||||
// https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
|
||||
constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
|
||||
const auto sub_a{fmt::format("{}u-{}", s32_max, a)};
|
||||
const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
|
||||
const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
|
||||
ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
ctx.uses_cc_carry = true;
|
||||
ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
|
||||
ctx.AddU1("{}=carry!=0;", *carry);
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
ctx.Add("{}={}+{};", result, a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint({}*{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint(int({})/int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}/{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=abs(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}=int({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '&');
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '|');
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '^');
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view insert, std::string_view offset, std::string_view count) {
|
||||
ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitfieldReverse({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitCount({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=~{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(uint({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}=={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}!={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}={}==0;", *zero, result);
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=int({})<0;", *sign, result);
|
||||
sign->Invalidate();
|
||||
}
|
||||
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
char lop) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}={}{}{};", result, a, lop, b);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
// Compute the overflow CC first as it requires the original operand values,
|
||||
// which may be overwritten by the result of the addition
|
||||
if (IR::Inst * overflow{inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
|
||||
// https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
|
||||
constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
|
||||
const auto sub_a{fmt::format("{}u-{}", s32_max, a)};
|
||||
const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
|
||||
const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
|
||||
ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
ctx.uses_cc_carry = true;
|
||||
ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
|
||||
ctx.AddU1("{}=carry!=0;", *carry);
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
ctx.Add("{}={}+{};", result, a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint({}*{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint(int({})/int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}/{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=abs(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}=int({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '&');
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '|');
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '^');
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view insert, std::string_view offset, std::string_view count) {
|
||||
ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitfieldReverse({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitCount({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=~{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(uint({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}=={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}!={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}||{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}&&{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}^^{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=!{};", inst, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}||{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}&&{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}^^{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=!{};", inst, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,201 +1,201 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
|
||||
std::string_view value, std::string_view bit_offset, u32 num_bits) {
|
||||
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
|
||||
ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x2("{}=uvec2(0);", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x4("{}=uvec4(0);", inst);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal32({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal64({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal128({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
|
||||
ctx.stage_name, binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
|
||||
"+12)>>2]);",
|
||||
inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
|
||||
offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
|
||||
binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
|
||||
std::string_view value, std::string_view bit_offset, u32 num_bits) {
|
||||
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
|
||||
ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x2("{}=uvec2(0);", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x4("{}=uvec4(0);", inst);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal32({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal64({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal128({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
|
||||
ctx.stage_name, binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
|
||||
"+12)>>2]);",
|
||||
inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
|
||||
offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
|
||||
binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,100 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
|
||||
std::string_view bit_offset, u32 num_bits) {
|
||||
const auto smem{fmt::format("smem[{}>>2]", offset)};
|
||||
ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=smem[{}>>2];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
|
||||
offset, offset, offset, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={};", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
|
||||
ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
|
||||
std::string_view bit_offset, u32 num_bits) {
|
||||
const auto smem{fmt::format("smem[{}>>2]", offset)};
|
||||
ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=smem[{}>>2];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
|
||||
offset, offset, offset, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={};", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
|
||||
ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
void InitializeOutputVaryings(EmitContext& ctx) {
|
||||
if (ctx.uses_geometry_passthrough) {
|
||||
return;
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
ctx.Add("gl_Position=vec4(0,0,0,1);");
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!ctx.info.stores.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto& info_array{ctx.output_generics.at(index)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
size_t element{};
|
||||
while (element < info_array.size()) {
|
||||
const auto& info{info_array.at(element)};
|
||||
const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
|
||||
switch (info.num_components) {
|
||||
case 1: {
|
||||
const char value{element == 3 ? '1' : '0'};
|
||||
ctx.Add("{}={}.f;", varying_name, value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
if (element + info.num_components < 4) {
|
||||
ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
|
||||
} else {
|
||||
// last element is the w component, must be initialized to 1
|
||||
const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
|
||||
ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
ctx.Add("{}=vec4(0,0,0,1);", varying_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
element += info.num_components;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.var_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.var_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{*phi_value.InstRecursive()};
|
||||
const auto phi_type{phi.Type()};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi_type);
|
||||
}
|
||||
const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
|
||||
const auto val_reg{ctx.var_alloc.Consume(value)};
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
void InitializeOutputVaryings(EmitContext& ctx) {
|
||||
if (ctx.uses_geometry_passthrough) {
|
||||
return;
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
ctx.Add("gl_Position=vec4(0,0,0,1);");
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!ctx.info.stores.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto& info_array{ctx.output_generics.at(index)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
size_t element{};
|
||||
while (element < info_array.size()) {
|
||||
const auto& info{info_array.at(element)};
|
||||
const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
|
||||
switch (info.num_components) {
|
||||
case 1: {
|
||||
const char value{element == 3 ? '1' : '0'};
|
||||
ctx.Add("{}={}.f;", varying_name, value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
if (element + info.num_components < 4) {
|
||||
ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
|
||||
} else {
|
||||
// last element is the w component, must be initialized to 1
|
||||
const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
|
||||
ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
ctx.Add("{}=vec4(0,0,0,1);", varying_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
element += info.num_components;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.var_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.var_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{*phi_value.InstRecursive()};
|
||||
const auto phi_type{phi.Type()};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi_type);
|
||||
}
|
||||
const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
|
||||
const auto val_reg{ctx.var_alloc.Consume(value)};
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=false;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU64("{}=0u;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=false;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU64("{}=0u;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,242 +1,242 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
|
||||
|
||||
void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
|
||||
return fmt::format("({}&{})", thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
|
||||
std::string_view not_seg_mask) {
|
||||
return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
|
||||
std::string_view value, std::string_view index,
|
||||
[[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
|
||||
const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
|
||||
ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
}
|
||||
|
||||
std::string_view BallotIndex(EmitContext& ctx) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ".x";
|
||||
}
|
||||
return "[gl_SubGroupInvocationARB>>5]";
|
||||
}
|
||||
|
||||
std::string GetMask(EmitContext& ctx, std::string_view mask) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
return fmt::format("uint(uvec2({}){})", mask, ballot_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
|
||||
const auto max_thread_id{
|
||||
ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
|
||||
|
||||
const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
|
||||
std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle) {
|
||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||
const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
|
||||
const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
|
||||
ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
|
||||
|
||||
void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
|
||||
return fmt::format("({}&{})", thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
|
||||
std::string_view not_seg_mask) {
|
||||
return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
|
||||
std::string_view value, std::string_view index,
|
||||
[[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
|
||||
const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
|
||||
ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
}
|
||||
|
||||
std::string_view BallotIndex(EmitContext& ctx) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ".x";
|
||||
}
|
||||
return "[gl_SubGroupInvocationARB>>5]";
|
||||
}
|
||||
|
||||
std::string GetMask(EmitContext& ctx, std::string_view mask) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
return fmt::format("uint(uvec2({}){})", mask, ballot_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
|
||||
const auto max_thread_id{
|
||||
ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
|
||||
|
||||
const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
|
||||
std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle) {
|
||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||
const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
|
||||
const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
|
||||
ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +1,174 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
struct GenericElementInfo {
|
||||
std::string name;
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
struct TextureImageDefinition {
|
||||
u32 binding;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <GlslVarType type, typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
const auto var_def{var_alloc.AddDefine(inst, type)};
|
||||
if (var_def.empty()) {
|
||||
// skip assigment.
|
||||
code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
|
||||
} else {
|
||||
code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
|
||||
}
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U1>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F16x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string header;
|
||||
std::string code;
|
||||
VarAlloc var_alloc;
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view position_name = "gl_Position";
|
||||
|
||||
std::vector<TextureImageDefinition> texture_buffers;
|
||||
std::vector<TextureImageDefinition> image_buffers;
|
||||
std::vector<TextureImageDefinition> textures;
|
||||
std::vector<TextureImageDefinition> images;
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
|
||||
bool uses_y_direction{};
|
||||
bool uses_cc_carry{};
|
||||
bool uses_geometry_passthrough{};
|
||||
|
||||
private:
|
||||
void SetupExtensions();
|
||||
void DefineConstantBuffers(Bindings& bindings);
|
||||
void DefineConstantBufferIndirect();
|
||||
void DefineStorageBuffers(Bindings& bindings);
|
||||
void DefineGenericOutput(size_t index, u32 invocations);
|
||||
void DefineHelperFunctions();
|
||||
void DefineConstants();
|
||||
std::string DefineGlobalMemoryFunctions();
|
||||
void SetupImages(Bindings& bindings);
|
||||
void SetupTextures(Bindings& bindings);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
struct GenericElementInfo {
|
||||
std::string name;
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
struct TextureImageDefinition {
|
||||
u32 binding;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <GlslVarType type, typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
const auto var_def{var_alloc.AddDefine(inst, type)};
|
||||
if (var_def.empty()) {
|
||||
// skip assigment.
|
||||
code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
|
||||
} else {
|
||||
code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
|
||||
}
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U1>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F16x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string header;
|
||||
std::string code;
|
||||
VarAlloc var_alloc;
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view position_name = "gl_Position";
|
||||
|
||||
std::vector<TextureImageDefinition> texture_buffers;
|
||||
std::vector<TextureImageDefinition> image_buffers;
|
||||
std::vector<TextureImageDefinition> textures;
|
||||
std::vector<TextureImageDefinition> images;
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
|
||||
bool uses_y_direction{};
|
||||
bool uses_cc_carry{};
|
||||
bool uses_geometry_passthrough{};
|
||||
|
||||
private:
|
||||
void SetupExtensions();
|
||||
void DefineConstantBuffers(Bindings& bindings);
|
||||
void DefineConstantBufferIndirect();
|
||||
void DefineStorageBuffers(Bindings& bindings);
|
||||
void DefineGenericOutput(size_t index, u32 invocations);
|
||||
void DefineHelperFunctions();
|
||||
void DefineConstants();
|
||||
std::string DefineGlobalMemoryFunctions();
|
||||
void SetupImages(Bindings& bindings);
|
||||
void SetupTextures(Bindings& bindings);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,306 +1,306 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string TypePrefix(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "b_";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16x2_";
|
||||
case GlslVarType::U32:
|
||||
return "u_";
|
||||
case GlslVarType::F32:
|
||||
return "f_";
|
||||
case GlslVarType::U64:
|
||||
return "u64_";
|
||||
case GlslVarType::F64:
|
||||
return "d_";
|
||||
case GlslVarType::U32x2:
|
||||
return "u2_";
|
||||
case GlslVarType::F32x2:
|
||||
return "f2_";
|
||||
case GlslVarType::U32x3:
|
||||
return "u3_";
|
||||
case GlslVarType::F32x3:
|
||||
return "f3_";
|
||||
case GlslVarType::U32x4:
|
||||
return "u4_";
|
||||
case GlslVarType::F32x4:
|
||||
return "f4_";
|
||||
case GlslVarType::PrecF32:
|
||||
return "pf_";
|
||||
case GlslVarType::PrecF64:
|
||||
return "pd_";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatFloat(std::string_view value, IR::Type type) {
|
||||
// TODO: Confirm FP64 nan/inf
|
||||
if (type == IR::Type::F32) {
|
||||
if (value == "nan") {
|
||||
return "utof(0x7fc00000)";
|
||||
}
|
||||
if (value == "inf") {
|
||||
return "utof(0x7f800000)";
|
||||
}
|
||||
if (value == "-inf") {
|
||||
return "utof(0xff800000)";
|
||||
}
|
||||
}
|
||||
if (value.find_first_of('e') != std::string_view::npos) {
|
||||
// scientific notation
|
||||
const auto cast{type == IR::Type::F32 ? "float" : "double"};
|
||||
return fmt::format("{}({})", cast, value);
|
||||
}
|
||||
const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
|
||||
const bool needs_suffix{!value.ends_with('f')};
|
||||
const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
|
||||
return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
|
||||
}
|
||||
|
||||
std::string MakeImm(const IR::Value& value) {
|
||||
switch (value.Type()) {
|
||||
case IR::Type::U1:
|
||||
return fmt::format("{}", value.U1() ? "true" : "false");
|
||||
case IR::Type::U32:
|
||||
return fmt::format("{}u", value.U32());
|
||||
case IR::Type::F32:
|
||||
return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
|
||||
case IR::Type::U64:
|
||||
return fmt::format("{}ul", value.U64());
|
||||
case IR::Type::F64:
|
||||
return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
|
||||
case IR::Type::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
|
||||
const auto prefix{TypePrefix(type)};
|
||||
return fmt::format("{}{}", prefix, index);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Representation(Id id) const {
|
||||
return Representation(id.index, id.type);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
Id id{};
|
||||
id.type.Assign(type);
|
||||
GetUseTracker(type).uses_temp = true;
|
||||
inst.SetDefinition<Id>(id);
|
||||
return 't' + Representation(inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
|
||||
return Define(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
|
||||
return AddDefine(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Consume(const IR::Value& value) {
|
||||
return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
|
||||
}
|
||||
|
||||
std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
inst.DestructiveRemoveUsage();
|
||||
if (!inst.HasUses()) {
|
||||
Free(inst.Definition<Id>());
|
||||
}
|
||||
return Representation(inst.Definition<Id>());
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(IR::Type type) const {
|
||||
return GetGlslType(RegType(type));
|
||||
}
|
||||
|
||||
Id VarAlloc::Alloc(GlslVarType type) {
|
||||
auto& use_tracker{GetUseTracker(type)};
|
||||
const auto num_vars{use_tracker.var_use.size()};
|
||||
for (size_t var = 0; var < num_vars; ++var) {
|
||||
if (use_tracker.var_use[var]) {
|
||||
continue;
|
||||
}
|
||||
use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
|
||||
use_tracker.var_use[var] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(var));
|
||||
return ret;
|
||||
}
|
||||
// Allocate a new variable
|
||||
use_tracker.var_use.push_back(true);
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(use_tracker.num_used));
|
||||
++use_tracker.num_used;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VarAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid variable");
|
||||
}
|
||||
auto& use_tracker{GetUseTracker(id.type)};
|
||||
use_tracker.var_use[id.index] = false;
|
||||
}
|
||||
|
||||
GlslVarType VarAlloc::RegType(IR::Type type) const {
|
||||
switch (type) {
|
||||
case IR::Type::U1:
|
||||
return GlslVarType::U1;
|
||||
case IR::Type::U32:
|
||||
return GlslVarType::U32;
|
||||
case IR::Type::F32:
|
||||
return GlslVarType::F32;
|
||||
case IR::Type::U64:
|
||||
return GlslVarType::U64;
|
||||
case IR::Type::F64:
|
||||
return GlslVarType::F64;
|
||||
default:
|
||||
throw NotImplementedException("IR type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "bool";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16vec2";
|
||||
case GlslVarType::U32:
|
||||
return "uint";
|
||||
case GlslVarType::F32:
|
||||
case GlslVarType::PrecF32:
|
||||
return "float";
|
||||
case GlslVarType::U64:
|
||||
return "uint64_t";
|
||||
case GlslVarType::F64:
|
||||
case GlslVarType::PrecF64:
|
||||
return "double";
|
||||
case GlslVarType::U32x2:
|
||||
return "uvec2";
|
||||
case GlslVarType::F32x2:
|
||||
return "vec2";
|
||||
case GlslVarType::U32x3:
|
||||
return "uvec3";
|
||||
case GlslVarType::F32x3:
|
||||
return "vec3";
|
||||
case GlslVarType::U32x4:
|
||||
return "uvec4";
|
||||
case GlslVarType::F32x4:
|
||||
return "vec4";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string TypePrefix(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "b_";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16x2_";
|
||||
case GlslVarType::U32:
|
||||
return "u_";
|
||||
case GlslVarType::F32:
|
||||
return "f_";
|
||||
case GlslVarType::U64:
|
||||
return "u64_";
|
||||
case GlslVarType::F64:
|
||||
return "d_";
|
||||
case GlslVarType::U32x2:
|
||||
return "u2_";
|
||||
case GlslVarType::F32x2:
|
||||
return "f2_";
|
||||
case GlslVarType::U32x3:
|
||||
return "u3_";
|
||||
case GlslVarType::F32x3:
|
||||
return "f3_";
|
||||
case GlslVarType::U32x4:
|
||||
return "u4_";
|
||||
case GlslVarType::F32x4:
|
||||
return "f4_";
|
||||
case GlslVarType::PrecF32:
|
||||
return "pf_";
|
||||
case GlslVarType::PrecF64:
|
||||
return "pd_";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatFloat(std::string_view value, IR::Type type) {
|
||||
// TODO: Confirm FP64 nan/inf
|
||||
if (type == IR::Type::F32) {
|
||||
if (value == "nan") {
|
||||
return "utof(0x7fc00000)";
|
||||
}
|
||||
if (value == "inf") {
|
||||
return "utof(0x7f800000)";
|
||||
}
|
||||
if (value == "-inf") {
|
||||
return "utof(0xff800000)";
|
||||
}
|
||||
}
|
||||
if (value.find_first_of('e') != std::string_view::npos) {
|
||||
// scientific notation
|
||||
const auto cast{type == IR::Type::F32 ? "float" : "double"};
|
||||
return fmt::format("{}({})", cast, value);
|
||||
}
|
||||
const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
|
||||
const bool needs_suffix{!value.ends_with('f')};
|
||||
const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
|
||||
return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
|
||||
}
|
||||
|
||||
std::string MakeImm(const IR::Value& value) {
|
||||
switch (value.Type()) {
|
||||
case IR::Type::U1:
|
||||
return fmt::format("{}", value.U1() ? "true" : "false");
|
||||
case IR::Type::U32:
|
||||
return fmt::format("{}u", value.U32());
|
||||
case IR::Type::F32:
|
||||
return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
|
||||
case IR::Type::U64:
|
||||
return fmt::format("{}ul", value.U64());
|
||||
case IR::Type::F64:
|
||||
return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
|
||||
case IR::Type::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
|
||||
const auto prefix{TypePrefix(type)};
|
||||
return fmt::format("{}{}", prefix, index);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Representation(Id id) const {
|
||||
return Representation(id.index, id.type);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
Id id{};
|
||||
id.type.Assign(type);
|
||||
GetUseTracker(type).uses_temp = true;
|
||||
inst.SetDefinition<Id>(id);
|
||||
return 't' + Representation(inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
|
||||
return Define(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
|
||||
return AddDefine(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Consume(const IR::Value& value) {
|
||||
return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
|
||||
}
|
||||
|
||||
std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
inst.DestructiveRemoveUsage();
|
||||
if (!inst.HasUses()) {
|
||||
Free(inst.Definition<Id>());
|
||||
}
|
||||
return Representation(inst.Definition<Id>());
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(IR::Type type) const {
|
||||
return GetGlslType(RegType(type));
|
||||
}
|
||||
|
||||
Id VarAlloc::Alloc(GlslVarType type) {
|
||||
auto& use_tracker{GetUseTracker(type)};
|
||||
const auto num_vars{use_tracker.var_use.size()};
|
||||
for (size_t var = 0; var < num_vars; ++var) {
|
||||
if (use_tracker.var_use[var]) {
|
||||
continue;
|
||||
}
|
||||
use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
|
||||
use_tracker.var_use[var] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(var));
|
||||
return ret;
|
||||
}
|
||||
// Allocate a new variable
|
||||
use_tracker.var_use.push_back(true);
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(use_tracker.num_used));
|
||||
++use_tracker.num_used;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VarAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid variable");
|
||||
}
|
||||
auto& use_tracker{GetUseTracker(id.type)};
|
||||
use_tracker.var_use[id.index] = false;
|
||||
}
|
||||
|
||||
GlslVarType VarAlloc::RegType(IR::Type type) const {
|
||||
switch (type) {
|
||||
case IR::Type::U1:
|
||||
return GlslVarType::U1;
|
||||
case IR::Type::U32:
|
||||
return GlslVarType::U32;
|
||||
case IR::Type::F32:
|
||||
return GlslVarType::F32;
|
||||
case IR::Type::U64:
|
||||
return GlslVarType::U64;
|
||||
case IR::Type::F64:
|
||||
return GlslVarType::F64;
|
||||
default:
|
||||
throw NotImplementedException("IR type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "bool";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16vec2";
|
||||
case GlslVarType::U32:
|
||||
return "uint";
|
||||
case GlslVarType::F32:
|
||||
case GlslVarType::PrecF32:
|
||||
return "float";
|
||||
case GlslVarType::U64:
|
||||
return "uint64_t";
|
||||
case GlslVarType::F64:
|
||||
case GlslVarType::PrecF64:
|
||||
return "double";
|
||||
case GlslVarType::U32x2:
|
||||
return "uvec2";
|
||||
case GlslVarType::F32x2:
|
||||
return "vec2";
|
||||
case GlslVarType::U32x3:
|
||||
return "uvec3";
|
||||
case GlslVarType::F32x3:
|
||||
return "vec3";
|
||||
case GlslVarType::U32x4:
|
||||
return "uvec4";
|
||||
case GlslVarType::F32x4:
|
||||
return "vec4";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
enum class Type;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
enum class GlslVarType : u32 {
|
||||
U1,
|
||||
F16x2,
|
||||
U32,
|
||||
F32,
|
||||
U64,
|
||||
F64,
|
||||
U32x2,
|
||||
F32x2,
|
||||
U32x3,
|
||||
F32x3,
|
||||
U32x4,
|
||||
F32x4,
|
||||
PrecF32,
|
||||
PrecF64,
|
||||
Void,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 4, GlslVarType> type;
|
||||
BitField<6, 26, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
class VarAlloc {
|
||||
public:
|
||||
struct UseTracker {
|
||||
bool uses_temp{};
|
||||
size_t num_used{};
|
||||
std::vector<bool> var_use;
|
||||
};
|
||||
|
||||
/// Used for explicit usages of variables, may revert to temporaries
|
||||
std::string Define(IR::Inst& inst, GlslVarType type);
|
||||
std::string Define(IR::Inst& inst, IR::Type type);
|
||||
|
||||
/// Used to assign variables used by the IR. May return a blank string if
|
||||
/// the instruction's result is unused in the IR.
|
||||
std::string AddDefine(IR::Inst& inst, GlslVarType type);
|
||||
std::string PhiDefine(IR::Inst& inst, IR::Type type);
|
||||
|
||||
std::string Consume(const IR::Value& value);
|
||||
std::string ConsumeInst(IR::Inst& inst);
|
||||
|
||||
std::string GetGlslType(GlslVarType type) const;
|
||||
std::string GetGlslType(IR::Type type) const;
|
||||
|
||||
const UseTracker& GetUseTracker(GlslVarType type) const;
|
||||
std::string Representation(u32 index, GlslVarType type) const;
|
||||
|
||||
private:
|
||||
GlslVarType RegType(IR::Type type) const;
|
||||
Id Alloc(GlslVarType type);
|
||||
void Free(Id id);
|
||||
UseTracker& GetUseTracker(GlslVarType type);
|
||||
std::string Representation(Id id) const;
|
||||
|
||||
UseTracker var_bool{};
|
||||
UseTracker var_f16x2{};
|
||||
UseTracker var_u32{};
|
||||
UseTracker var_u32x2{};
|
||||
UseTracker var_u32x3{};
|
||||
UseTracker var_u32x4{};
|
||||
UseTracker var_f32{};
|
||||
UseTracker var_f32x2{};
|
||||
UseTracker var_f32x3{};
|
||||
UseTracker var_f32x4{};
|
||||
UseTracker var_u64{};
|
||||
UseTracker var_f64{};
|
||||
UseTracker var_precf32{};
|
||||
UseTracker var_precf64{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
enum class Type;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
enum class GlslVarType : u32 {
|
||||
U1,
|
||||
F16x2,
|
||||
U32,
|
||||
F32,
|
||||
U64,
|
||||
F64,
|
||||
U32x2,
|
||||
F32x2,
|
||||
U32x3,
|
||||
F32x3,
|
||||
U32x4,
|
||||
F32x4,
|
||||
PrecF32,
|
||||
PrecF64,
|
||||
Void,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 4, GlslVarType> type;
|
||||
BitField<6, 26, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
class VarAlloc {
|
||||
public:
|
||||
struct UseTracker {
|
||||
bool uses_temp{};
|
||||
size_t num_used{};
|
||||
std::vector<bool> var_use;
|
||||
};
|
||||
|
||||
/// Used for explicit usages of variables, may revert to temporaries
|
||||
std::string Define(IR::Inst& inst, GlslVarType type);
|
||||
std::string Define(IR::Inst& inst, IR::Type type);
|
||||
|
||||
/// Used to assign variables used by the IR. May return a blank string if
|
||||
/// the instruction's result is unused in the IR.
|
||||
std::string AddDefine(IR::Inst& inst, GlslVarType type);
|
||||
std::string PhiDefine(IR::Inst& inst, IR::Type type);
|
||||
|
||||
std::string Consume(const IR::Value& value);
|
||||
std::string ConsumeInst(IR::Inst& inst);
|
||||
|
||||
std::string GetGlslType(GlslVarType type) const;
|
||||
std::string GetGlslType(IR::Type type) const;
|
||||
|
||||
const UseTracker& GetUseTracker(GlslVarType type) const;
|
||||
std::string Representation(u32 index, GlslVarType type) const;
|
||||
|
||||
private:
|
||||
GlslVarType RegType(IR::Type type) const;
|
||||
Id Alloc(GlslVarType type);
|
||||
void Free(Id id);
|
||||
UseTracker& GetUseTracker(GlslVarType type);
|
||||
std::string Representation(Id id) const;
|
||||
|
||||
UseTracker var_bool{};
|
||||
UseTracker var_f16x2{};
|
||||
UseTracker var_u32{};
|
||||
UseTracker var_u32x2{};
|
||||
UseTracker var_u32x3{};
|
||||
UseTracker var_u32x4{};
|
||||
UseTracker var_f32{};
|
||||
UseTracker var_f32x2{};
|
||||
UseTracker var_f32x3{};
|
||||
UseTracker var_f32x4{};
|
||||
UseTracker var_u64{};
|
||||
UseTracker var_f64{};
|
||||
UseTracker var_precf32{};
|
||||
UseTracker var_precf64{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
constexpr u32 NUM_TEXTURE_SCALING_WORDS = 4;
|
||||
constexpr u32 NUM_IMAGE_SCALING_WORDS = 2;
|
||||
constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
|
||||
NUM_TEXTURE_SCALING_WORDS + NUM_IMAGE_SCALING_WORDS;
|
||||
|
||||
struct RescalingLayout {
|
||||
alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
|
||||
alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
|
||||
u32 down_factor;
|
||||
};
|
||||
constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
|
||||
constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
|
||||
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitSPIRV(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
constexpr u32 NUM_TEXTURE_SCALING_WORDS = 4;
|
||||
constexpr u32 NUM_IMAGE_SCALING_WORDS = 2;
|
||||
constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
|
||||
NUM_TEXTURE_SCALING_WORDS + NUM_IMAGE_SCALING_WORDS;
|
||||
|
||||
struct RescalingLayout {
|
||||
alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
|
||||
alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
|
||||
u32 down_factor;
|
||||
};
|
||||
constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
|
||||
constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
|
||||
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitSPIRV(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void MemoryBarrier(EmitContext& ctx, spv::Scope scope) {
|
||||
const auto semantics{
|
||||
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory |
|
||||
spv::MemorySemanticsMask::ImageMemory};
|
||||
ctx.OpMemoryBarrier(ctx.Const(static_cast<u32>(scope)), ctx.Const(static_cast<u32>(semantics)));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
const auto execution{spv::Scope::Workgroup};
|
||||
const auto memory{spv::Scope::Workgroup};
|
||||
const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory};
|
||||
ctx.OpControlBarrier(ctx.Const(static_cast<u32>(execution)),
|
||||
ctx.Const(static_cast<u32>(memory)),
|
||||
ctx.Const(static_cast<u32>(memory_semantics)));
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Workgroup);
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Device);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void MemoryBarrier(EmitContext& ctx, spv::Scope scope) {
|
||||
const auto semantics{
|
||||
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory |
|
||||
spv::MemorySemanticsMask::ImageMemory};
|
||||
ctx.OpMemoryBarrier(ctx.Const(static_cast<u32>(scope)), ctx.Const(static_cast<u32>(semantics)));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
const auto execution{spv::Scope::Workgroup};
|
||||
const auto memory{spv::Scope::Workgroup};
|
||||
const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory};
|
||||
ctx.OpControlBarrier(ctx.Const(static_cast<u32>(execution)),
|
||||
ctx.Const(static_cast<u32>(memory)),
|
||||
ctx.Const(static_cast<u32>(memory_semantics)));
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Workgroup);
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Device);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitPackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitUnpackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F16[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitPackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitUnpackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F16[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
if (ctx.profile.support_demote_to_helper_invocation) {
|
||||
ctx.OpDemoteToHelperInvocationEXT();
|
||||
} else {
|
||||
const Id kill_label{ctx.OpLabel()};
|
||||
const Id impossible_label{ctx.OpLabel()};
|
||||
ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label);
|
||||
ctx.AddLabel(kill_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(impossible_label);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
if (ctx.profile.support_demote_to_helper_invocation) {
|
||||
ctx.OpDemoteToHelperInvocationEXT();
|
||||
} else {
|
||||
const Id kill_label{ctx.OpLabel()};
|
||||
const Id impossible_label{ctx.OpLabel()};
|
||||
ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label);
|
||||
ctx.AddLabel(kill_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(impossible_label);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,268 +1,268 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id ExtractU16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.S16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractU8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpUConvert(ctx.U8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpSConvert(ctx.S8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitConvertS16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS32F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
return ctx.OpBitcast(ctx.U32[1], ctx.OpConvertFToS(ctx.S32[1], value));
|
||||
} else {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id ExtractU16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.S16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractU8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpUConvert(ctx.U8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpSConvert(ctx.S8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitConvertS16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS32F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
return ctx.OpBitcast(ctx.U32[1], ctx.OpConvertFToS(ctx.S32[1], value));
|
||||
} else {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,395 +1,395 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
|
||||
const auto flags{inst->Flags<IR::FpControl>()};
|
||||
if (flags.no_contraction) {
|
||||
ctx.Decorate(op, spv::Decoration::NoContraction);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
Id Clamp(EmitContext& ctx, Id type, Id value, Id zero, Id one) {
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one);
|
||||
} else {
|
||||
return ctx.OpFClamp(type, value, zero, one);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPOrdNotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id comp{ctx.OpFOrdEqual(ctx.U1, lhs, rhs)};
|
||||
const Id lhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, lhs))};
|
||||
const Id rhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, rhs))};
|
||||
return ctx.OpLogicalAnd(ctx.U1, ctx.OpLogicalAnd(ctx.U1, comp, lhs_not_nan), rhs_not_nan);
|
||||
} else {
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPUnordCompare(Id (EmitContext::*comp_func)(Id, Id, Id), EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id lhs_nan{ctx.OpIsNan(ctx.U1, lhs)};
|
||||
const Id rhs_nan{ctx.OpIsNan(ctx.U1, rhs)};
|
||||
const Id comp{(ctx.*comp_func)(ctx.U1, lhs, rhs)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpLogicalOr(ctx.U1, comp, lhs_nan), rhs_nan);
|
||||
} else {
|
||||
return (ctx.*comp_func)(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitFPAbs16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMax64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPNeg16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSin(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSin(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCos(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCos(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPExp2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpExp2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPLog2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLog2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F32[1], ctx.Const(1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSqrt(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate16(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
|
||||
const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
|
||||
return Clamp(ctx, ctx.F16[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate32(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Const(f32{0.0})};
|
||||
const Id one{ctx.Const(f32{1.0})};
|
||||
return Clamp(ctx, ctx.F32[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate64(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
|
||||
const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
|
||||
return Clamp(ctx, ctx.F64[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F16[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F32[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F64[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
|
||||
const auto flags{inst->Flags<IR::FpControl>()};
|
||||
if (flags.no_contraction) {
|
||||
ctx.Decorate(op, spv::Decoration::NoContraction);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
Id Clamp(EmitContext& ctx, Id type, Id value, Id zero, Id one) {
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one);
|
||||
} else {
|
||||
return ctx.OpFClamp(type, value, zero, one);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPOrdNotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id comp{ctx.OpFOrdEqual(ctx.U1, lhs, rhs)};
|
||||
const Id lhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, lhs))};
|
||||
const Id rhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, rhs))};
|
||||
return ctx.OpLogicalAnd(ctx.U1, ctx.OpLogicalAnd(ctx.U1, comp, lhs_not_nan), rhs_not_nan);
|
||||
} else {
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPUnordCompare(Id (EmitContext::*comp_func)(Id, Id, Id), EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id lhs_nan{ctx.OpIsNan(ctx.U1, lhs)};
|
||||
const Id rhs_nan{ctx.OpIsNan(ctx.U1, rhs)};
|
||||
const Id comp{(ctx.*comp_func)(ctx.U1, lhs, rhs)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpLogicalOr(ctx.U1, comp, lhs_nan), rhs_nan);
|
||||
} else {
|
||||
return (ctx.*comp_func)(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitFPAbs16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMax64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPNeg16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSin(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSin(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCos(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCos(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPExp2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpExp2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPLog2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLog2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F32[1], ctx.Const(1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSqrt(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate16(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
|
||||
const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
|
||||
return Clamp(ctx, ctx.F16[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate32(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Const(f32{0.0})};
|
||||
const Id one{ctx.Const(f32{1.0})};
|
||||
return Clamp(ctx, ctx.F32[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate64(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
|
||||
const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
|
||||
return Clamp(ctx, ctx.F64[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F16[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F32[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F64[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,182 +1,182 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
|
||||
return def.id;
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(index.U32())};
|
||||
return def.id;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
|
||||
const Id semantics{ctx.u32_zero_value};
|
||||
return {scope, semantics};
|
||||
}
|
||||
|
||||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{Image(ctx, index, info)};
|
||||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange);
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
|
||||
return def.id;
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(index.U32())};
|
||||
return def.id;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
|
||||
const Id semantics{ctx.u32_zero_value};
|
||||
return {scope, semantics};
|
||||
}
|
||||
|
||||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{Image(ctx, index, info)};
|
||||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange);
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,277 +1,277 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value));
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value));
|
||||
sign->Invalidate();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
Id result{};
|
||||
if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
|
||||
const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
|
||||
result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
|
||||
|
||||
const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
|
||||
carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value));
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
result = ctx.OpIAdd(ctx.U32[1], a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
|
||||
// https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
|
||||
constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
|
||||
const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)};
|
||||
const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
|
||||
|
||||
const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)};
|
||||
const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)};
|
||||
const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)};
|
||||
overflow->SetDefinition(carry_flag);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIAdd(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitISub32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitISub64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIMul(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSDiv32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpSDiv(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitUDiv32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUDiv(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitINeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitINeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitIAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSAbs(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
|
||||
return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
|
||||
}
|
||||
|
||||
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitReverse32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitReverse(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitCount32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitCount(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpNot(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindSMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindSMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindUMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMin(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMax(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
min = ctx.OpBitcast(ctx.S32[1], min);
|
||||
max = ctx.OpBitcast(ctx.S32[1], max);
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.S32[1], value, min, max);
|
||||
}
|
||||
result = ctx.OpBitcast(ctx.U32[1], result);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpUClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpIEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value));
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value));
|
||||
sign->Invalidate();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
Id result{};
|
||||
if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
|
||||
const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
|
||||
result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
|
||||
|
||||
const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
|
||||
carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value));
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
result = ctx.OpIAdd(ctx.U32[1], a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
|
||||
// https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
|
||||
constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
|
||||
const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)};
|
||||
const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
|
||||
|
||||
const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)};
|
||||
const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)};
|
||||
const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)};
|
||||
overflow->SetDefinition(carry_flag);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIAdd(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitISub32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitISub64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIMul(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSDiv32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpSDiv(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitUDiv32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUDiv(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitINeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitINeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitIAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSAbs(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
|
||||
return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
|
||||
}
|
||||
|
||||
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitReverse32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitReverse(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitCount32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitCount(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpNot(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindSMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindSMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindUMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMin(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMax(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
min = ctx.OpBitcast(ctx.S32[1], min);
|
||||
max = ctx.OpBitcast(ctx.S32[1], max);
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.S32[1], value, min, max);
|
||||
}
|
||||
result = ctx.OpBitcast(ctx.U32[1], result);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpUClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpIEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalOr(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalAnd(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalNotEqual(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalNot(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLogicalNot(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalOr(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalAnd(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalNotEqual(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalNot(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLogicalNot(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,275 +1,275 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size,
|
||||
u32 index_offset = 0) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
Id index{ctx.Def(offset)};
|
||||
if (shift != 0) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
if (index_offset != 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size, index_offset)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
return ctx.OpLoad(result_type, pointer);
|
||||
}
|
||||
|
||||
Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
u32 index_offset = 0) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[1], ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
|
||||
void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
u32 index_offset = 0) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u, 0u, 0u);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return LoadStorage32(ctx, binding, offset);
|
||||
}
|
||||
|
||||
Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[2], ctx.storage_types.U32x2,
|
||||
sizeof(u32[2]), &StorageDefinitions::U32x2);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[4], ctx.storage_types.U32x4,
|
||||
sizeof(u32[4]), &StorageDefinitions::U32x4);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1),
|
||||
LoadStorage32(ctx, binding, offset, 2),
|
||||
LoadStorage32(ctx, binding, offset, 3));
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage32(ctx, binding, offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x2, sizeof(u32[2]),
|
||||
&StorageDefinitions::U32x2);
|
||||
} else {
|
||||
for (u32 index = 0; index < 2; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x4, sizeof(u32[4]),
|
||||
&StorageDefinitions::U32x4);
|
||||
} else {
|
||||
for (u32 index = 0; index < 4; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size,
|
||||
u32 index_offset = 0) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
Id index{ctx.Def(offset)};
|
||||
if (shift != 0) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
if (index_offset != 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size, index_offset)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
return ctx.OpLoad(result_type, pointer);
|
||||
}
|
||||
|
||||
Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
u32 index_offset = 0) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[1], ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
|
||||
void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
u32 index_offset = 0) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u, 0u, 0u);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return LoadStorage32(ctx, binding, offset);
|
||||
}
|
||||
|
||||
Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[2], ctx.storage_types.U32x2,
|
||||
sizeof(u32[2]), &StorageDefinitions::U32x2);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[4], ctx.storage_types.U32x4,
|
||||
sizeof(u32[4]), &StorageDefinitions::U32x4);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1),
|
||||
LoadStorage32(ctx, binding, offset, 2),
|
||||
LoadStorage32(ctx, binding, offset, 3));
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage32(ctx, binding, offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x2, sizeof(u32[2]),
|
||||
&StorageDefinitions::U32x2);
|
||||
} else {
|
||||
for (u32 index = 0; index < 2; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x4, sizeof(u32[4]),
|
||||
&StorageDefinitions::U32x4);
|
||||
} else {
|
||||
for (u32 index = 0; index < 4; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U1, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU8(EmitContext&, Id, Id, Id) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U16, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U64, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F16[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F64[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U1, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU8(EmitContext&, Id, Id, Id) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U16, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U64, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F16[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F64[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,173 +1,173 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Pointer(EmitContext& ctx, Id pointer_type, Id array, Id offset, u32 shift) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
return ctx.OpAccessChain(pointer_type, array, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id Word(EmitContext& ctx, Id offset) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) {
|
||||
const Id shift{ctx.OpShiftLeftLogical(ctx.U32[1], offset, ctx.Const(3U))};
|
||||
const Id bit{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(mask))};
|
||||
const Id count_id{ctx.Const(count)};
|
||||
return {bit, count_id};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
} else {
|
||||
return Word(ctx, offset);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
return ctx.OpLoad(ctx.U32[2], pointer);
|
||||
} else {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)};
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer),
|
||||
ctx.OpLoad(ctx.U32[1], rhs_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
return ctx.OpLoad(ctx.U32[4], pointer);
|
||||
}
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
std::array<Id, 4> values{};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
values[i] = ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], values);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u8_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u16_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||
Id pointer{};
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
||||
} else {
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset);
|
||||
}
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)};
|
||||
ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U));
|
||||
ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U));
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Pointer(EmitContext& ctx, Id pointer_type, Id array, Id offset, u32 shift) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
return ctx.OpAccessChain(pointer_type, array, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id Word(EmitContext& ctx, Id offset) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) {
|
||||
const Id shift{ctx.OpShiftLeftLogical(ctx.U32[1], offset, ctx.Const(3U))};
|
||||
const Id bit{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(mask))};
|
||||
const Id count_id{ctx.Const(count)};
|
||||
return {bit, count_id};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
} else {
|
||||
return Word(ctx, offset);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
return ctx.OpLoad(ctx.U32[2], pointer);
|
||||
} else {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)};
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer),
|
||||
ctx.OpLoad(ctx.U32[1], rhs_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
return ctx.OpLoad(ctx.U32[4], pointer);
|
||||
}
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
std::array<Id, 4> values{};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
values[i] = ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], values);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u8_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u16_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||
Id pointer{};
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
||||
} else {
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset);
|
||||
}
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)};
|
||||
ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U));
|
||||
ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U));
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,150 +1,150 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void ConvertDepthMode(EmitContext& ctx) {
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
|
||||
const Id z{ctx.OpCompositeExtract(type, position, 2u)};
|
||||
const Id w{ctx.OpCompositeExtract(type, position, 3u)};
|
||||
const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))};
|
||||
const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)};
|
||||
ctx.OpStore(ctx.output_position, vector);
|
||||
}
|
||||
|
||||
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
||||
if (ctx.runtime_info.fixed_state_point_size) {
|
||||
const float point_size{*ctx.runtime_info.fixed_state_point_size};
|
||||
ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
|
||||
}
|
||||
}
|
||||
|
||||
Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one,
|
||||
Id default_vector) {
|
||||
switch (num_components) {
|
||||
case 1:
|
||||
return element == 3 ? one : zero;
|
||||
case 2:
|
||||
return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero);
|
||||
case 3:
|
||||
return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero);
|
||||
case 4:
|
||||
return default_vector;
|
||||
}
|
||||
throw InvalidArgument("Bad element");
|
||||
}
|
||||
|
||||
Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1, Id operand_2) {
|
||||
switch (comparison) {
|
||||
case CompareFunction::Never:
|
||||
return ctx.false_value;
|
||||
case CompareFunction::Less:
|
||||
return ctx.OpFOrdLessThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Equal:
|
||||
return ctx.OpFOrdEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::LessThanEqual:
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Greater:
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::NotEqual:
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::GreaterThanEqual:
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Always:
|
||||
return ctx.true_value;
|
||||
}
|
||||
throw InvalidArgument("Comparison function {}", comparison);
|
||||
}
|
||||
|
||||
void AlphaTest(EmitContext& ctx) {
|
||||
if (!ctx.runtime_info.alpha_test_func) {
|
||||
return;
|
||||
}
|
||||
const auto comparison{*ctx.runtime_info.alpha_test_func};
|
||||
if (comparison == CompareFunction::Always) {
|
||||
return;
|
||||
}
|
||||
if (!Sirit::ValidId(ctx.frag_color[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id rt0_color{ctx.OpLoad(ctx.F32[4], ctx.frag_color[0])};
|
||||
const Id alpha{ctx.OpCompositeExtract(type, rt0_color, 3u)};
|
||||
|
||||
const Id true_label{ctx.OpLabel()};
|
||||
const Id discard_label{ctx.OpLabel()};
|
||||
const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)};
|
||||
const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
|
||||
|
||||
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(condition, true_label, discard_label);
|
||||
ctx.AddLabel(discard_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(true_label);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB) {
|
||||
const Id zero{ctx.Const(0.0f)};
|
||||
const Id one{ctx.Const(1.0f)};
|
||||
const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)};
|
||||
ctx.OpStore(ctx.output_position, default_vector);
|
||||
for (const auto& info : ctx.output_generics) {
|
||||
if (info[0].num_components == 0) {
|
||||
continue;
|
||||
}
|
||||
u32 element{0};
|
||||
while (element < 4) {
|
||||
const auto& element_info{info[element]};
|
||||
const u32 num{element_info.num_components};
|
||||
const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)};
|
||||
ctx.OpStore(element_info.id, value);
|
||||
element += num;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
AlphaTest(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEmitStreamVertex(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEmitStreamVertex(ctx.u32_zero_value);
|
||||
}
|
||||
// Restore fixed pipeline point size after emitting the vertex
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEndStreamPrimitive(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEndStreamPrimitive(ctx.u32_zero_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void ConvertDepthMode(EmitContext& ctx) {
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
|
||||
const Id z{ctx.OpCompositeExtract(type, position, 2u)};
|
||||
const Id w{ctx.OpCompositeExtract(type, position, 3u)};
|
||||
const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))};
|
||||
const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)};
|
||||
ctx.OpStore(ctx.output_position, vector);
|
||||
}
|
||||
|
||||
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
||||
if (ctx.runtime_info.fixed_state_point_size) {
|
||||
const float point_size{*ctx.runtime_info.fixed_state_point_size};
|
||||
ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
|
||||
}
|
||||
}
|
||||
|
||||
Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one,
|
||||
Id default_vector) {
|
||||
switch (num_components) {
|
||||
case 1:
|
||||
return element == 3 ? one : zero;
|
||||
case 2:
|
||||
return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero);
|
||||
case 3:
|
||||
return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero);
|
||||
case 4:
|
||||
return default_vector;
|
||||
}
|
||||
throw InvalidArgument("Bad element");
|
||||
}
|
||||
|
||||
Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1, Id operand_2) {
|
||||
switch (comparison) {
|
||||
case CompareFunction::Never:
|
||||
return ctx.false_value;
|
||||
case CompareFunction::Less:
|
||||
return ctx.OpFOrdLessThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Equal:
|
||||
return ctx.OpFOrdEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::LessThanEqual:
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Greater:
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::NotEqual:
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::GreaterThanEqual:
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Always:
|
||||
return ctx.true_value;
|
||||
}
|
||||
throw InvalidArgument("Comparison function {}", comparison);
|
||||
}
|
||||
|
||||
void AlphaTest(EmitContext& ctx) {
|
||||
if (!ctx.runtime_info.alpha_test_func) {
|
||||
return;
|
||||
}
|
||||
const auto comparison{*ctx.runtime_info.alpha_test_func};
|
||||
if (comparison == CompareFunction::Always) {
|
||||
return;
|
||||
}
|
||||
if (!Sirit::ValidId(ctx.frag_color[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id rt0_color{ctx.OpLoad(ctx.F32[4], ctx.frag_color[0])};
|
||||
const Id alpha{ctx.OpCompositeExtract(type, rt0_color, 3u)};
|
||||
|
||||
const Id true_label{ctx.OpLabel()};
|
||||
const Id discard_label{ctx.OpLabel()};
|
||||
const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)};
|
||||
const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
|
||||
|
||||
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(condition, true_label, discard_label);
|
||||
ctx.AddLabel(discard_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(true_label);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB) {
|
||||
const Id zero{ctx.Const(0.0f)};
|
||||
const Id one{ctx.Const(1.0f)};
|
||||
const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)};
|
||||
ctx.OpStore(ctx.output_position, default_vector);
|
||||
for (const auto& info : ctx.output_generics) {
|
||||
if (info[0].num_components == 0) {
|
||||
continue;
|
||||
}
|
||||
u32 element{0};
|
||||
while (element < 4) {
|
||||
const auto& element_info{info[element]};
|
||||
const u32 num{element_info.num_components};
|
||||
const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)};
|
||||
ctx.OpStore(element_info.id, value);
|
||||
element += num;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
AlphaTest(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEmitStreamVertex(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEmitStreamVertex(ctx.u32_zero_value);
|
||||
}
|
||||
// Restore fixed pipeline point size after emitting the vertex
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEndStreamPrimitive(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEndStreamPrimitive(ctx.u32_zero_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitUndefU1(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U1);
|
||||
}
|
||||
|
||||
Id EmitUndefU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU32(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U32[1]);
|
||||
}
|
||||
|
||||
Id EmitUndefU64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitUndefU1(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U1);
|
||||
}
|
||||
|
||||
Id EmitUndefU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU32(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U32[1]);
|
||||
}
|
||||
|
||||
Id EmitUndefU64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,231 +1,231 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id GetThreadId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
||||
Id WarpExtract(EmitContext& ctx, Id value) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
|
||||
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
|
||||
}
|
||||
|
||||
Id LoadMask(EmitContext& ctx, Id mask) {
|
||||
const Id value{ctx.OpLoad(ctx.U32[4], mask)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], value, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, value);
|
||||
}
|
||||
|
||||
void SetInBoundsFlag(IR::Inst* inst, Id result) {
|
||||
IR::Inst* const in_bounds{inst->GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
in_bounds->SetDefinition(result);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
Id ComputeMinThreadId(EmitContext& ctx, Id thread_id, Id segmentation_mask) {
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
Id ComputeMaxThreadId(EmitContext& ctx, Id min_thread_id, Id clamp, Id not_seg_mask) {
|
||||
return ctx.OpBitwiseOr(ctx.U32[1], min_thread_id,
|
||||
ctx.OpBitwiseAnd(ctx.U32[1], clamp, not_seg_mask));
|
||||
}
|
||||
|
||||
Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
return ctx.OpSelect(ctx.U32[1], in_range,
|
||||
ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
|
||||
}
|
||||
|
||||
Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
const Id id{GetThreadId(ctx)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return id;
|
||||
}
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], id, ctx.Const(31U));
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpIEqual(ctx.U1, lhs, active_mask);
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAnyKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
|
||||
ctx.OpIEqual(ctx.U1, lhs, active_mask));
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, ballot);
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
|
||||
const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
|
||||
clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
|
||||
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
|
||||
const Id modifier_a{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_a, mask)};
|
||||
const Id modifier_b{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_b, mask)};
|
||||
|
||||
const Id result_a{ctx.OpFMul(ctx.F32[1], op_a, modifier_a)};
|
||||
const Id result_b{ctx.OpFMul(ctx.F32[1], op_b, modifier_b)};
|
||||
return ctx.OpFAdd(ctx.F32[1], result_a, result_b);
|
||||
}
|
||||
|
||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdxCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id GetThreadId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
||||
Id WarpExtract(EmitContext& ctx, Id value) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
|
||||
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
|
||||
}
|
||||
|
||||
Id LoadMask(EmitContext& ctx, Id mask) {
|
||||
const Id value{ctx.OpLoad(ctx.U32[4], mask)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], value, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, value);
|
||||
}
|
||||
|
||||
void SetInBoundsFlag(IR::Inst* inst, Id result) {
|
||||
IR::Inst* const in_bounds{inst->GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
in_bounds->SetDefinition(result);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
Id ComputeMinThreadId(EmitContext& ctx, Id thread_id, Id segmentation_mask) {
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
Id ComputeMaxThreadId(EmitContext& ctx, Id min_thread_id, Id clamp, Id not_seg_mask) {
|
||||
return ctx.OpBitwiseOr(ctx.U32[1], min_thread_id,
|
||||
ctx.OpBitwiseAnd(ctx.U32[1], clamp, not_seg_mask));
|
||||
}
|
||||
|
||||
Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
return ctx.OpSelect(ctx.U32[1], in_range,
|
||||
ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
|
||||
}
|
||||
|
||||
Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
const Id id{GetThreadId(ctx)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return id;
|
||||
}
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], id, ctx.Const(31U));
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpIEqual(ctx.U1, lhs, active_mask);
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAnyKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
|
||||
ctx.OpIEqual(ctx.U1, lhs, active_mask));
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, ballot);
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
|
||||
const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
|
||||
clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
|
||||
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
|
||||
const Id modifier_a{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_a, mask)};
|
||||
const Id modifier_b{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_b, mask)};
|
||||
|
||||
const Id result_a{ctx.OpFMul(ctx.F32[1], op_a, modifier_a)};
|
||||
const Id result_b{ctx.OpFMul(ctx.F32[1], op_b, modifier_b)};
|
||||
return ctx.OpFAdd(ctx.F32[1], result_a, result_b);
|
||||
}
|
||||
|
||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdxCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,326 +1,326 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
using Sirit::Id;
|
||||
|
||||
class VectorTypes {
|
||||
public:
|
||||
void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name);
|
||||
|
||||
[[nodiscard]] Id operator[](size_t size) const noexcept {
|
||||
return defs[size - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Id, 4> defs{};
|
||||
};
|
||||
|
||||
struct TextureDefinition {
|
||||
Id id;
|
||||
Id sampled_type;
|
||||
Id pointer_type;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct TextureBufferDefinition {
|
||||
Id id;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageBufferDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct UniformDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinition {
|
||||
Id array{};
|
||||
Id element{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinitions {
|
||||
StorageTypeDefinition U8{};
|
||||
StorageTypeDefinition S8{};
|
||||
StorageTypeDefinition U16{};
|
||||
StorageTypeDefinition S16{};
|
||||
StorageTypeDefinition U32{};
|
||||
StorageTypeDefinition U64{};
|
||||
StorageTypeDefinition F32{};
|
||||
StorageTypeDefinition U32x2{};
|
||||
StorageTypeDefinition U32x4{};
|
||||
};
|
||||
|
||||
struct StorageDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U64{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct GenericElementInfo {
|
||||
Id id{};
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
class EmitContext final : public Sirit::Module {
|
||||
public:
|
||||
explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& binding);
|
||||
~EmitContext();
|
||||
|
||||
[[nodiscard]] Id Def(const IR::Value& value);
|
||||
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2) {
|
||||
return ConstantComposite(U32[2], Const(element_1), Const(element_2));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3) {
|
||||
return ConstantComposite(U32[3], Const(element_1), Const(element_2), Const(element_3));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3, u32 element_4) {
|
||||
return ConstantComposite(U32[4], Const(element_1), Const(element_2), Const(element_3),
|
||||
Const(element_4));
|
||||
}
|
||||
|
||||
Id SConst(s32 value) {
|
||||
return Constant(S32[1], value);
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2) {
|
||||
return ConstantComposite(S32[2], SConst(element_1), SConst(element_2));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3) {
|
||||
return ConstantComposite(S32[3], SConst(element_1), SConst(element_2), SConst(element_3));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3, s32 element_4) {
|
||||
return ConstantComposite(S32[4], SConst(element_1), SConst(element_2), SConst(element_3),
|
||||
SConst(element_4));
|
||||
}
|
||||
|
||||
Id Const(f32 value) {
|
||||
return Constant(F32[1], value);
|
||||
}
|
||||
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
Stage stage{};
|
||||
|
||||
Id void_id{};
|
||||
Id U1{};
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U64{};
|
||||
VectorTypes F32;
|
||||
VectorTypes U32;
|
||||
VectorTypes S32;
|
||||
VectorTypes F16;
|
||||
VectorTypes F64;
|
||||
|
||||
Id true_value{};
|
||||
Id false_value{};
|
||||
Id u32_zero_value{};
|
||||
Id f32_zero_value{};
|
||||
|
||||
UniformDefinitions uniform_types;
|
||||
StorageTypeDefinitions storage_types;
|
||||
|
||||
Id private_u32{};
|
||||
|
||||
Id shared_u8{};
|
||||
Id shared_u16{};
|
||||
Id shared_u32{};
|
||||
Id shared_u64{};
|
||||
Id shared_u32x2{};
|
||||
Id shared_u32x4{};
|
||||
|
||||
Id input_f32{};
|
||||
Id input_u32{};
|
||||
Id input_s32{};
|
||||
|
||||
Id output_f32{};
|
||||
Id output_u32{};
|
||||
|
||||
Id image_buffer_type{};
|
||||
Id sampled_texture_buffer_type{};
|
||||
Id image_u32{};
|
||||
|
||||
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
|
||||
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
|
||||
std::vector<TextureBufferDefinition> texture_buffers;
|
||||
std::vector<ImageBufferDefinition> image_buffers;
|
||||
std::vector<TextureDefinition> textures;
|
||||
std::vector<ImageDefinition> images;
|
||||
|
||||
Id workgroup_id{};
|
||||
Id local_invocation_id{};
|
||||
Id invocation_id{};
|
||||
Id sample_id{};
|
||||
Id is_helper_invocation{};
|
||||
Id subgroup_local_invocation_id{};
|
||||
Id subgroup_mask_eq{};
|
||||
Id subgroup_mask_lt{};
|
||||
Id subgroup_mask_le{};
|
||||
Id subgroup_mask_gt{};
|
||||
Id subgroup_mask_ge{};
|
||||
Id instance_id{};
|
||||
Id instance_index{};
|
||||
Id base_instance{};
|
||||
Id vertex_id{};
|
||||
Id vertex_index{};
|
||||
Id base_vertex{};
|
||||
Id front_face{};
|
||||
Id point_coord{};
|
||||
Id tess_coord{};
|
||||
Id clip_distances{};
|
||||
Id layer{};
|
||||
Id viewport_index{};
|
||||
Id viewport_mask{};
|
||||
Id primitive_id{};
|
||||
|
||||
Id fswzadd_lut_a{};
|
||||
Id fswzadd_lut_b{};
|
||||
|
||||
Id indexed_load_func{};
|
||||
Id indexed_store_func{};
|
||||
|
||||
Id rescaling_uniform_constant{};
|
||||
Id rescaling_push_constants{};
|
||||
Id rescaling_textures_type{};
|
||||
Id rescaling_images_type{};
|
||||
u32 rescaling_textures_member_index{};
|
||||
u32 rescaling_images_member_index{};
|
||||
u32 rescaling_downfactor_member_index{};
|
||||
u32 texture_rescaling_index{};
|
||||
u32 image_rescaling_index{};
|
||||
|
||||
Id local_memory{};
|
||||
|
||||
Id shared_memory_u8{};
|
||||
Id shared_memory_u16{};
|
||||
Id shared_memory_u32{};
|
||||
Id shared_memory_u64{};
|
||||
Id shared_memory_u32x2{};
|
||||
Id shared_memory_u32x4{};
|
||||
|
||||
Id shared_memory_u32_type{};
|
||||
|
||||
Id shared_store_u8_func{};
|
||||
Id shared_store_u16_func{};
|
||||
Id increment_cas_shared{};
|
||||
Id increment_cas_ssbo{};
|
||||
Id decrement_cas_shared{};
|
||||
Id decrement_cas_ssbo{};
|
||||
Id f32_add_cas{};
|
||||
Id f16x2_add_cas{};
|
||||
Id f16x2_min_cas{};
|
||||
Id f16x2_max_cas{};
|
||||
Id f32x2_add_cas{};
|
||||
Id f32x2_min_cas{};
|
||||
Id f32x2_max_cas{};
|
||||
|
||||
Id load_global_func_u32{};
|
||||
Id load_global_func_u32x2{};
|
||||
Id load_global_func_u32x4{};
|
||||
Id write_global_func_u32{};
|
||||
Id write_global_func_u32x2{};
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
Id output_tess_level_inner{};
|
||||
std::array<Id, 30> patches{};
|
||||
|
||||
std::array<Id, 8> frag_color{};
|
||||
Id sample_mask{};
|
||||
Id frag_depth{};
|
||||
|
||||
std::vector<Id> interfaces;
|
||||
|
||||
Id load_const_func_u8{};
|
||||
Id load_const_func_u16{};
|
||||
Id load_const_func_u32{};
|
||||
Id load_const_func_f32{};
|
||||
Id load_const_func_u32x2{};
|
||||
Id load_const_func_u32x4{};
|
||||
|
||||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
void DefineCommonConstants();
|
||||
void DefineInterfaces(const IR::Program& program);
|
||||
void DefineLocalMemory(const IR::Program& program);
|
||||
void DefineSharedMemory(const IR::Program& program);
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
void DefineConstantBuffers(const Info& info, u32& binding);
|
||||
void DefineConstantBufferIndirectFunctions(const Info& info);
|
||||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineImages(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineAttributeMemAccess(const Info& info);
|
||||
void DefineGlobalMemoryFunctions(const Info& info);
|
||||
void DefineRescalingInput(const Info& info);
|
||||
void DefineRescalingInputPushConstant();
|
||||
void DefineRescalingInputUniformConstant();
|
||||
|
||||
void DefineInputs(const IR::Program& program);
|
||||
void DefineOutputs(const IR::Program& program);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
using Sirit::Id;
|
||||
|
||||
class VectorTypes {
|
||||
public:
|
||||
void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name);
|
||||
|
||||
[[nodiscard]] Id operator[](size_t size) const noexcept {
|
||||
return defs[size - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Id, 4> defs{};
|
||||
};
|
||||
|
||||
struct TextureDefinition {
|
||||
Id id;
|
||||
Id sampled_type;
|
||||
Id pointer_type;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct TextureBufferDefinition {
|
||||
Id id;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageBufferDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct UniformDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinition {
|
||||
Id array{};
|
||||
Id element{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinitions {
|
||||
StorageTypeDefinition U8{};
|
||||
StorageTypeDefinition S8{};
|
||||
StorageTypeDefinition U16{};
|
||||
StorageTypeDefinition S16{};
|
||||
StorageTypeDefinition U32{};
|
||||
StorageTypeDefinition U64{};
|
||||
StorageTypeDefinition F32{};
|
||||
StorageTypeDefinition U32x2{};
|
||||
StorageTypeDefinition U32x4{};
|
||||
};
|
||||
|
||||
struct StorageDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U64{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct GenericElementInfo {
|
||||
Id id{};
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
class EmitContext final : public Sirit::Module {
|
||||
public:
|
||||
explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& binding);
|
||||
~EmitContext();
|
||||
|
||||
[[nodiscard]] Id Def(const IR::Value& value);
|
||||
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2) {
|
||||
return ConstantComposite(U32[2], Const(element_1), Const(element_2));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3) {
|
||||
return ConstantComposite(U32[3], Const(element_1), Const(element_2), Const(element_3));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3, u32 element_4) {
|
||||
return ConstantComposite(U32[4], Const(element_1), Const(element_2), Const(element_3),
|
||||
Const(element_4));
|
||||
}
|
||||
|
||||
Id SConst(s32 value) {
|
||||
return Constant(S32[1], value);
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2) {
|
||||
return ConstantComposite(S32[2], SConst(element_1), SConst(element_2));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3) {
|
||||
return ConstantComposite(S32[3], SConst(element_1), SConst(element_2), SConst(element_3));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3, s32 element_4) {
|
||||
return ConstantComposite(S32[4], SConst(element_1), SConst(element_2), SConst(element_3),
|
||||
SConst(element_4));
|
||||
}
|
||||
|
||||
Id Const(f32 value) {
|
||||
return Constant(F32[1], value);
|
||||
}
|
||||
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
Stage stage{};
|
||||
|
||||
Id void_id{};
|
||||
Id U1{};
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U64{};
|
||||
VectorTypes F32;
|
||||
VectorTypes U32;
|
||||
VectorTypes S32;
|
||||
VectorTypes F16;
|
||||
VectorTypes F64;
|
||||
|
||||
Id true_value{};
|
||||
Id false_value{};
|
||||
Id u32_zero_value{};
|
||||
Id f32_zero_value{};
|
||||
|
||||
UniformDefinitions uniform_types;
|
||||
StorageTypeDefinitions storage_types;
|
||||
|
||||
Id private_u32{};
|
||||
|
||||
Id shared_u8{};
|
||||
Id shared_u16{};
|
||||
Id shared_u32{};
|
||||
Id shared_u64{};
|
||||
Id shared_u32x2{};
|
||||
Id shared_u32x4{};
|
||||
|
||||
Id input_f32{};
|
||||
Id input_u32{};
|
||||
Id input_s32{};
|
||||
|
||||
Id output_f32{};
|
||||
Id output_u32{};
|
||||
|
||||
Id image_buffer_type{};
|
||||
Id sampled_texture_buffer_type{};
|
||||
Id image_u32{};
|
||||
|
||||
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
|
||||
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
|
||||
std::vector<TextureBufferDefinition> texture_buffers;
|
||||
std::vector<ImageBufferDefinition> image_buffers;
|
||||
std::vector<TextureDefinition> textures;
|
||||
std::vector<ImageDefinition> images;
|
||||
|
||||
Id workgroup_id{};
|
||||
Id local_invocation_id{};
|
||||
Id invocation_id{};
|
||||
Id sample_id{};
|
||||
Id is_helper_invocation{};
|
||||
Id subgroup_local_invocation_id{};
|
||||
Id subgroup_mask_eq{};
|
||||
Id subgroup_mask_lt{};
|
||||
Id subgroup_mask_le{};
|
||||
Id subgroup_mask_gt{};
|
||||
Id subgroup_mask_ge{};
|
||||
Id instance_id{};
|
||||
Id instance_index{};
|
||||
Id base_instance{};
|
||||
Id vertex_id{};
|
||||
Id vertex_index{};
|
||||
Id base_vertex{};
|
||||
Id front_face{};
|
||||
Id point_coord{};
|
||||
Id tess_coord{};
|
||||
Id clip_distances{};
|
||||
Id layer{};
|
||||
Id viewport_index{};
|
||||
Id viewport_mask{};
|
||||
Id primitive_id{};
|
||||
|
||||
Id fswzadd_lut_a{};
|
||||
Id fswzadd_lut_b{};
|
||||
|
||||
Id indexed_load_func{};
|
||||
Id indexed_store_func{};
|
||||
|
||||
Id rescaling_uniform_constant{};
|
||||
Id rescaling_push_constants{};
|
||||
Id rescaling_textures_type{};
|
||||
Id rescaling_images_type{};
|
||||
u32 rescaling_textures_member_index{};
|
||||
u32 rescaling_images_member_index{};
|
||||
u32 rescaling_downfactor_member_index{};
|
||||
u32 texture_rescaling_index{};
|
||||
u32 image_rescaling_index{};
|
||||
|
||||
Id local_memory{};
|
||||
|
||||
Id shared_memory_u8{};
|
||||
Id shared_memory_u16{};
|
||||
Id shared_memory_u32{};
|
||||
Id shared_memory_u64{};
|
||||
Id shared_memory_u32x2{};
|
||||
Id shared_memory_u32x4{};
|
||||
|
||||
Id shared_memory_u32_type{};
|
||||
|
||||
Id shared_store_u8_func{};
|
||||
Id shared_store_u16_func{};
|
||||
Id increment_cas_shared{};
|
||||
Id increment_cas_ssbo{};
|
||||
Id decrement_cas_shared{};
|
||||
Id decrement_cas_ssbo{};
|
||||
Id f32_add_cas{};
|
||||
Id f16x2_add_cas{};
|
||||
Id f16x2_min_cas{};
|
||||
Id f16x2_max_cas{};
|
||||
Id f32x2_add_cas{};
|
||||
Id f32x2_min_cas{};
|
||||
Id f32x2_max_cas{};
|
||||
|
||||
Id load_global_func_u32{};
|
||||
Id load_global_func_u32x2{};
|
||||
Id load_global_func_u32x4{};
|
||||
Id write_global_func_u32{};
|
||||
Id write_global_func_u32x2{};
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
Id output_tess_level_inner{};
|
||||
std::array<Id, 30> patches{};
|
||||
|
||||
std::array<Id, 8> frag_color{};
|
||||
Id sample_mask{};
|
||||
Id frag_depth{};
|
||||
|
||||
std::vector<Id> interfaces;
|
||||
|
||||
Id load_const_func_u8{};
|
||||
Id load_const_func_u16{};
|
||||
Id load_const_func_u32{};
|
||||
Id load_const_func_f32{};
|
||||
Id load_const_func_u32x2{};
|
||||
Id load_const_func_u32x4{};
|
||||
|
||||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
void DefineCommonConstants();
|
||||
void DefineInterfaces(const IR::Program& program);
|
||||
void DefineLocalMemory(const IR::Program& program);
|
||||
void DefineSharedMemory(const IR::Program& program);
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
void DefineConstantBuffers(const Info& info, u32& binding);
|
||||
void DefineConstantBufferIndirectFunctions(const Info& info);
|
||||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineImages(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineAttributeMemAccess(const Info& info);
|
||||
void DefineGlobalMemoryFunctions(const Info& info);
|
||||
void DefineRescalingInput(const Info& info);
|
||||
void DefineRescalingInputPushConstant();
|
||||
void DefineRescalingInputUniformConstant();
|
||||
|
||||
void DefineInputs(const IR::Program& program);
|
||||
void DefineOutputs(const IR::Program& program);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
virtual ~Environment() = default;
|
||||
|
||||
[[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) = 0;
|
||||
|
||||
[[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 LocalMemorySize() const = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 SharedMemorySize() const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
return sph;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::array<u32, 8>& GpPassthroughMask() const noexcept {
|
||||
return gp_passthrough_mask;
|
||||
}
|
||||
|
||||
[[nodiscard]] Stage ShaderStage() const noexcept {
|
||||
return stage;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 StartAddress() const noexcept {
|
||||
return start_address;
|
||||
}
|
||||
|
||||
protected:
|
||||
ProgramHeader sph{};
|
||||
std::array<u32, 8> gp_passthrough_mask{};
|
||||
Stage stage{};
|
||||
u32 start_address{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
virtual ~Environment() = default;
|
||||
|
||||
[[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) = 0;
|
||||
|
||||
[[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 LocalMemorySize() const = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 SharedMemorySize() const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
return sph;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::array<u32, 8>& GpPassthroughMask() const noexcept {
|
||||
return gp_passthrough_mask;
|
||||
}
|
||||
|
||||
[[nodiscard]] Stage ShaderStage() const noexcept {
|
||||
return stage;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 StartAddress() const noexcept {
|
||||
return start_address;
|
||||
}
|
||||
|
||||
protected:
|
||||
ProgramHeader sph{};
|
||||
std::array<u32, 8> gp_passthrough_mask{};
|
||||
Stage stage{};
|
||||
u32 start_address{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
class Exception : public std::exception {
|
||||
public:
|
||||
explicit Exception(std::string message) noexcept : err_message{std::move(message)} {}
|
||||
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return err_message.c_str();
|
||||
}
|
||||
|
||||
void Prepend(std::string_view prepend) {
|
||||
err_message.insert(0, prepend);
|
||||
}
|
||||
|
||||
void Append(std::string_view append) {
|
||||
err_message += append;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string err_message;
|
||||
};
|
||||
|
||||
class LogicError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit LogicError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class RuntimeError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit RuntimeError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class NotImplementedException : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit NotImplementedException(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {
|
||||
Append(" is not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidArgument : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit InvalidArgument(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
class Exception : public std::exception {
|
||||
public:
|
||||
explicit Exception(std::string message) noexcept : err_message{std::move(message)} {}
|
||||
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return err_message.c_str();
|
||||
}
|
||||
|
||||
void Prepend(std::string_view prepend) {
|
||||
err_message.insert(0, prepend);
|
||||
}
|
||||
|
||||
void Append(std::string_view append) {
|
||||
err_message += append;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string err_message;
|
||||
};
|
||||
|
||||
class LogicError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit LogicError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class RuntimeError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit RuntimeError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class NotImplementedException : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit NotImplementedException(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {
|
||||
Append(" is not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidArgument : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit InvalidArgument(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block;
|
||||
|
||||
struct AbstractSyntaxNode {
|
||||
enum class Type {
|
||||
Block,
|
||||
If,
|
||||
EndIf,
|
||||
Loop,
|
||||
Repeat,
|
||||
Break,
|
||||
Return,
|
||||
Unreachable,
|
||||
};
|
||||
union Data {
|
||||
Block* block;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* body;
|
||||
Block* merge;
|
||||
} if_node;
|
||||
struct {
|
||||
Block* merge;
|
||||
} end_if;
|
||||
struct {
|
||||
Block* body;
|
||||
Block* continue_block;
|
||||
Block* merge;
|
||||
} loop;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* loop_header;
|
||||
Block* merge;
|
||||
} repeat;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* merge;
|
||||
Block* skip;
|
||||
} break_node;
|
||||
};
|
||||
|
||||
Data data{};
|
||||
Type type{};
|
||||
};
|
||||
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block;
|
||||
|
||||
struct AbstractSyntaxNode {
|
||||
enum class Type {
|
||||
Block,
|
||||
If,
|
||||
EndIf,
|
||||
Loop,
|
||||
Repeat,
|
||||
Break,
|
||||
Return,
|
||||
Unreachable,
|
||||
};
|
||||
union Data {
|
||||
Block* block;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* body;
|
||||
Block* merge;
|
||||
} if_node;
|
||||
struct {
|
||||
Block* merge;
|
||||
} end_if;
|
||||
struct {
|
||||
Block* body;
|
||||
Block* continue_block;
|
||||
Block* merge;
|
||||
} loop;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* loop_header;
|
||||
Block* merge;
|
||||
} repeat;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* merge;
|
||||
Block* skip;
|
||||
} break_node;
|
||||
};
|
||||
|
||||
Data data{};
|
||||
Type type{};
|
||||
};
|
||||
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,453 +1,453 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Attribute attribute) noexcept {
|
||||
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
|
||||
}
|
||||
|
||||
u32 GenericAttributeIndex(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u;
|
||||
}
|
||||
|
||||
u32 GenericAttributeElement(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return static_cast<u32>(attribute) % 4;
|
||||
}
|
||||
|
||||
std::string NameOf(Attribute attribute) {
|
||||
switch (attribute) {
|
||||
case Attribute::PrimitiveId:
|
||||
return "PrimitiveId";
|
||||
case Attribute::Layer:
|
||||
return "Layer";
|
||||
case Attribute::ViewportIndex:
|
||||
return "ViewportIndex";
|
||||
case Attribute::PointSize:
|
||||
return "PointSize";
|
||||
case Attribute::PositionX:
|
||||
return "Position.X";
|
||||
case Attribute::PositionY:
|
||||
return "Position.Y";
|
||||
case Attribute::PositionZ:
|
||||
return "Position.Z";
|
||||
case Attribute::PositionW:
|
||||
return "Position.W";
|
||||
case Attribute::Generic0X:
|
||||
return "Generic[0].X";
|
||||
case Attribute::Generic0Y:
|
||||
return "Generic[0].Y";
|
||||
case Attribute::Generic0Z:
|
||||
return "Generic[0].Z";
|
||||
case Attribute::Generic0W:
|
||||
return "Generic[0].W";
|
||||
case Attribute::Generic1X:
|
||||
return "Generic[1].X";
|
||||
case Attribute::Generic1Y:
|
||||
return "Generic[1].Y";
|
||||
case Attribute::Generic1Z:
|
||||
return "Generic[1].Z";
|
||||
case Attribute::Generic1W:
|
||||
return "Generic[1].W";
|
||||
case Attribute::Generic2X:
|
||||
return "Generic[2].X";
|
||||
case Attribute::Generic2Y:
|
||||
return "Generic[2].Y";
|
||||
case Attribute::Generic2Z:
|
||||
return "Generic[2].Z";
|
||||
case Attribute::Generic2W:
|
||||
return "Generic[2].W";
|
||||
case Attribute::Generic3X:
|
||||
return "Generic[3].X";
|
||||
case Attribute::Generic3Y:
|
||||
return "Generic[3].Y";
|
||||
case Attribute::Generic3Z:
|
||||
return "Generic[3].Z";
|
||||
case Attribute::Generic3W:
|
||||
return "Generic[3].W";
|
||||
case Attribute::Generic4X:
|
||||
return "Generic[4].X";
|
||||
case Attribute::Generic4Y:
|
||||
return "Generic[4].Y";
|
||||
case Attribute::Generic4Z:
|
||||
return "Generic[4].Z";
|
||||
case Attribute::Generic4W:
|
||||
return "Generic[4].W";
|
||||
case Attribute::Generic5X:
|
||||
return "Generic[5].X";
|
||||
case Attribute::Generic5Y:
|
||||
return "Generic[5].Y";
|
||||
case Attribute::Generic5Z:
|
||||
return "Generic[5].Z";
|
||||
case Attribute::Generic5W:
|
||||
return "Generic[5].W";
|
||||
case Attribute::Generic6X:
|
||||
return "Generic[6].X";
|
||||
case Attribute::Generic6Y:
|
||||
return "Generic[6].Y";
|
||||
case Attribute::Generic6Z:
|
||||
return "Generic[6].Z";
|
||||
case Attribute::Generic6W:
|
||||
return "Generic[6].W";
|
||||
case Attribute::Generic7X:
|
||||
return "Generic[7].X";
|
||||
case Attribute::Generic7Y:
|
||||
return "Generic[7].Y";
|
||||
case Attribute::Generic7Z:
|
||||
return "Generic[7].Z";
|
||||
case Attribute::Generic7W:
|
||||
return "Generic[7].W";
|
||||
case Attribute::Generic8X:
|
||||
return "Generic[8].X";
|
||||
case Attribute::Generic8Y:
|
||||
return "Generic[8].Y";
|
||||
case Attribute::Generic8Z:
|
||||
return "Generic[8].Z";
|
||||
case Attribute::Generic8W:
|
||||
return "Generic[8].W";
|
||||
case Attribute::Generic9X:
|
||||
return "Generic[9].X";
|
||||
case Attribute::Generic9Y:
|
||||
return "Generic[9].Y";
|
||||
case Attribute::Generic9Z:
|
||||
return "Generic[9].Z";
|
||||
case Attribute::Generic9W:
|
||||
return "Generic[9].W";
|
||||
case Attribute::Generic10X:
|
||||
return "Generic[10].X";
|
||||
case Attribute::Generic10Y:
|
||||
return "Generic[10].Y";
|
||||
case Attribute::Generic10Z:
|
||||
return "Generic[10].Z";
|
||||
case Attribute::Generic10W:
|
||||
return "Generic[10].W";
|
||||
case Attribute::Generic11X:
|
||||
return "Generic[11].X";
|
||||
case Attribute::Generic11Y:
|
||||
return "Generic[11].Y";
|
||||
case Attribute::Generic11Z:
|
||||
return "Generic[11].Z";
|
||||
case Attribute::Generic11W:
|
||||
return "Generic[11].W";
|
||||
case Attribute::Generic12X:
|
||||
return "Generic[12].X";
|
||||
case Attribute::Generic12Y:
|
||||
return "Generic[12].Y";
|
||||
case Attribute::Generic12Z:
|
||||
return "Generic[12].Z";
|
||||
case Attribute::Generic12W:
|
||||
return "Generic[12].W";
|
||||
case Attribute::Generic13X:
|
||||
return "Generic[13].X";
|
||||
case Attribute::Generic13Y:
|
||||
return "Generic[13].Y";
|
||||
case Attribute::Generic13Z:
|
||||
return "Generic[13].Z";
|
||||
case Attribute::Generic13W:
|
||||
return "Generic[13].W";
|
||||
case Attribute::Generic14X:
|
||||
return "Generic[14].X";
|
||||
case Attribute::Generic14Y:
|
||||
return "Generic[14].Y";
|
||||
case Attribute::Generic14Z:
|
||||
return "Generic[14].Z";
|
||||
case Attribute::Generic14W:
|
||||
return "Generic[14].W";
|
||||
case Attribute::Generic15X:
|
||||
return "Generic[15].X";
|
||||
case Attribute::Generic15Y:
|
||||
return "Generic[15].Y";
|
||||
case Attribute::Generic15Z:
|
||||
return "Generic[15].Z";
|
||||
case Attribute::Generic15W:
|
||||
return "Generic[15].W";
|
||||
case Attribute::Generic16X:
|
||||
return "Generic[16].X";
|
||||
case Attribute::Generic16Y:
|
||||
return "Generic[16].Y";
|
||||
case Attribute::Generic16Z:
|
||||
return "Generic[16].Z";
|
||||
case Attribute::Generic16W:
|
||||
return "Generic[16].W";
|
||||
case Attribute::Generic17X:
|
||||
return "Generic[17].X";
|
||||
case Attribute::Generic17Y:
|
||||
return "Generic[17].Y";
|
||||
case Attribute::Generic17Z:
|
||||
return "Generic[17].Z";
|
||||
case Attribute::Generic17W:
|
||||
return "Generic[17].W";
|
||||
case Attribute::Generic18X:
|
||||
return "Generic[18].X";
|
||||
case Attribute::Generic18Y:
|
||||
return "Generic[18].Y";
|
||||
case Attribute::Generic18Z:
|
||||
return "Generic[18].Z";
|
||||
case Attribute::Generic18W:
|
||||
return "Generic[18].W";
|
||||
case Attribute::Generic19X:
|
||||
return "Generic[19].X";
|
||||
case Attribute::Generic19Y:
|
||||
return "Generic[19].Y";
|
||||
case Attribute::Generic19Z:
|
||||
return "Generic[19].Z";
|
||||
case Attribute::Generic19W:
|
||||
return "Generic[19].W";
|
||||
case Attribute::Generic20X:
|
||||
return "Generic[20].X";
|
||||
case Attribute::Generic20Y:
|
||||
return "Generic[20].Y";
|
||||
case Attribute::Generic20Z:
|
||||
return "Generic[20].Z";
|
||||
case Attribute::Generic20W:
|
||||
return "Generic[20].W";
|
||||
case Attribute::Generic21X:
|
||||
return "Generic[21].X";
|
||||
case Attribute::Generic21Y:
|
||||
return "Generic[21].Y";
|
||||
case Attribute::Generic21Z:
|
||||
return "Generic[21].Z";
|
||||
case Attribute::Generic21W:
|
||||
return "Generic[21].W";
|
||||
case Attribute::Generic22X:
|
||||
return "Generic[22].X";
|
||||
case Attribute::Generic22Y:
|
||||
return "Generic[22].Y";
|
||||
case Attribute::Generic22Z:
|
||||
return "Generic[22].Z";
|
||||
case Attribute::Generic22W:
|
||||
return "Generic[22].W";
|
||||
case Attribute::Generic23X:
|
||||
return "Generic[23].X";
|
||||
case Attribute::Generic23Y:
|
||||
return "Generic[23].Y";
|
||||
case Attribute::Generic23Z:
|
||||
return "Generic[23].Z";
|
||||
case Attribute::Generic23W:
|
||||
return "Generic[23].W";
|
||||
case Attribute::Generic24X:
|
||||
return "Generic[24].X";
|
||||
case Attribute::Generic24Y:
|
||||
return "Generic[24].Y";
|
||||
case Attribute::Generic24Z:
|
||||
return "Generic[24].Z";
|
||||
case Attribute::Generic24W:
|
||||
return "Generic[24].W";
|
||||
case Attribute::Generic25X:
|
||||
return "Generic[25].X";
|
||||
case Attribute::Generic25Y:
|
||||
return "Generic[25].Y";
|
||||
case Attribute::Generic25Z:
|
||||
return "Generic[25].Z";
|
||||
case Attribute::Generic25W:
|
||||
return "Generic[25].W";
|
||||
case Attribute::Generic26X:
|
||||
return "Generic[26].X";
|
||||
case Attribute::Generic26Y:
|
||||
return "Generic[26].Y";
|
||||
case Attribute::Generic26Z:
|
||||
return "Generic[26].Z";
|
||||
case Attribute::Generic26W:
|
||||
return "Generic[26].W";
|
||||
case Attribute::Generic27X:
|
||||
return "Generic[27].X";
|
||||
case Attribute::Generic27Y:
|
||||
return "Generic[27].Y";
|
||||
case Attribute::Generic27Z:
|
||||
return "Generic[27].Z";
|
||||
case Attribute::Generic27W:
|
||||
return "Generic[27].W";
|
||||
case Attribute::Generic28X:
|
||||
return "Generic[28].X";
|
||||
case Attribute::Generic28Y:
|
||||
return "Generic[28].Y";
|
||||
case Attribute::Generic28Z:
|
||||
return "Generic[28].Z";
|
||||
case Attribute::Generic28W:
|
||||
return "Generic[28].W";
|
||||
case Attribute::Generic29X:
|
||||
return "Generic[29].X";
|
||||
case Attribute::Generic29Y:
|
||||
return "Generic[29].Y";
|
||||
case Attribute::Generic29Z:
|
||||
return "Generic[29].Z";
|
||||
case Attribute::Generic29W:
|
||||
return "Generic[29].W";
|
||||
case Attribute::Generic30X:
|
||||
return "Generic[30].X";
|
||||
case Attribute::Generic30Y:
|
||||
return "Generic[30].Y";
|
||||
case Attribute::Generic30Z:
|
||||
return "Generic[30].Z";
|
||||
case Attribute::Generic30W:
|
||||
return "Generic[30].W";
|
||||
case Attribute::Generic31X:
|
||||
return "Generic[31].X";
|
||||
case Attribute::Generic31Y:
|
||||
return "Generic[31].Y";
|
||||
case Attribute::Generic31Z:
|
||||
return "Generic[31].Z";
|
||||
case Attribute::Generic31W:
|
||||
return "Generic[31].W";
|
||||
case Attribute::ColorFrontDiffuseR:
|
||||
return "ColorFrontDiffuse.R";
|
||||
case Attribute::ColorFrontDiffuseG:
|
||||
return "ColorFrontDiffuse.G";
|
||||
case Attribute::ColorFrontDiffuseB:
|
||||
return "ColorFrontDiffuse.B";
|
||||
case Attribute::ColorFrontDiffuseA:
|
||||
return "ColorFrontDiffuse.A";
|
||||
case Attribute::ColorFrontSpecularR:
|
||||
return "ColorFrontSpecular.R";
|
||||
case Attribute::ColorFrontSpecularG:
|
||||
return "ColorFrontSpecular.G";
|
||||
case Attribute::ColorFrontSpecularB:
|
||||
return "ColorFrontSpecular.B";
|
||||
case Attribute::ColorFrontSpecularA:
|
||||
return "ColorFrontSpecular.A";
|
||||
case Attribute::ColorBackDiffuseR:
|
||||
return "ColorBackDiffuse.R";
|
||||
case Attribute::ColorBackDiffuseG:
|
||||
return "ColorBackDiffuse.G";
|
||||
case Attribute::ColorBackDiffuseB:
|
||||
return "ColorBackDiffuse.B";
|
||||
case Attribute::ColorBackDiffuseA:
|
||||
return "ColorBackDiffuse.A";
|
||||
case Attribute::ColorBackSpecularR:
|
||||
return "ColorBackSpecular.R";
|
||||
case Attribute::ColorBackSpecularG:
|
||||
return "ColorBackSpecular.G";
|
||||
case Attribute::ColorBackSpecularB:
|
||||
return "ColorBackSpecular.B";
|
||||
case Attribute::ColorBackSpecularA:
|
||||
return "ColorBackSpecular.A";
|
||||
case Attribute::ClipDistance0:
|
||||
return "ClipDistance[0]";
|
||||
case Attribute::ClipDistance1:
|
||||
return "ClipDistance[1]";
|
||||
case Attribute::ClipDistance2:
|
||||
return "ClipDistance[2]";
|
||||
case Attribute::ClipDistance3:
|
||||
return "ClipDistance[3]";
|
||||
case Attribute::ClipDistance4:
|
||||
return "ClipDistance[4]";
|
||||
case Attribute::ClipDistance5:
|
||||
return "ClipDistance[5]";
|
||||
case Attribute::ClipDistance6:
|
||||
return "ClipDistance[6]";
|
||||
case Attribute::ClipDistance7:
|
||||
return "ClipDistance[7]";
|
||||
case Attribute::PointSpriteS:
|
||||
return "PointSprite.S";
|
||||
case Attribute::PointSpriteT:
|
||||
return "PointSprite.T";
|
||||
case Attribute::FogCoordinate:
|
||||
return "FogCoordinate";
|
||||
case Attribute::TessellationEvaluationPointU:
|
||||
return "TessellationEvaluationPoint.U";
|
||||
case Attribute::TessellationEvaluationPointV:
|
||||
return "TessellationEvaluationPoint.V";
|
||||
case Attribute::InstanceId:
|
||||
return "InstanceId";
|
||||
case Attribute::VertexId:
|
||||
return "VertexId";
|
||||
case Attribute::FixedFncTexture0S:
|
||||
return "FixedFncTexture[0].S";
|
||||
case Attribute::FixedFncTexture0T:
|
||||
return "FixedFncTexture[0].T";
|
||||
case Attribute::FixedFncTexture0R:
|
||||
return "FixedFncTexture[0].R";
|
||||
case Attribute::FixedFncTexture0Q:
|
||||
return "FixedFncTexture[0].Q";
|
||||
case Attribute::FixedFncTexture1S:
|
||||
return "FixedFncTexture[1].S";
|
||||
case Attribute::FixedFncTexture1T:
|
||||
return "FixedFncTexture[1].T";
|
||||
case Attribute::FixedFncTexture1R:
|
||||
return "FixedFncTexture[1].R";
|
||||
case Attribute::FixedFncTexture1Q:
|
||||
return "FixedFncTexture[1].Q";
|
||||
case Attribute::FixedFncTexture2S:
|
||||
return "FixedFncTexture[2].S";
|
||||
case Attribute::FixedFncTexture2T:
|
||||
return "FixedFncTexture[2].T";
|
||||
case Attribute::FixedFncTexture2R:
|
||||
return "FixedFncTexture[2].R";
|
||||
case Attribute::FixedFncTexture2Q:
|
||||
return "FixedFncTexture[2].Q";
|
||||
case Attribute::FixedFncTexture3S:
|
||||
return "FixedFncTexture[3].S";
|
||||
case Attribute::FixedFncTexture3T:
|
||||
return "FixedFncTexture[3].T";
|
||||
case Attribute::FixedFncTexture3R:
|
||||
return "FixedFncTexture[3].R";
|
||||
case Attribute::FixedFncTexture3Q:
|
||||
return "FixedFncTexture[3].Q";
|
||||
case Attribute::FixedFncTexture4S:
|
||||
return "FixedFncTexture[4].S";
|
||||
case Attribute::FixedFncTexture4T:
|
||||
return "FixedFncTexture[4].T";
|
||||
case Attribute::FixedFncTexture4R:
|
||||
return "FixedFncTexture[4].R";
|
||||
case Attribute::FixedFncTexture4Q:
|
||||
return "FixedFncTexture[4].Q";
|
||||
case Attribute::FixedFncTexture5S:
|
||||
return "FixedFncTexture[5].S";
|
||||
case Attribute::FixedFncTexture5T:
|
||||
return "FixedFncTexture[5].T";
|
||||
case Attribute::FixedFncTexture5R:
|
||||
return "FixedFncTexture[5].R";
|
||||
case Attribute::FixedFncTexture5Q:
|
||||
return "FixedFncTexture[5].Q";
|
||||
case Attribute::FixedFncTexture6S:
|
||||
return "FixedFncTexture[6].S";
|
||||
case Attribute::FixedFncTexture6T:
|
||||
return "FixedFncTexture[6].T";
|
||||
case Attribute::FixedFncTexture6R:
|
||||
return "FixedFncTexture[6].R";
|
||||
case Attribute::FixedFncTexture6Q:
|
||||
return "FixedFncTexture[6].Q";
|
||||
case Attribute::FixedFncTexture7S:
|
||||
return "FixedFncTexture[7].S";
|
||||
case Attribute::FixedFncTexture7T:
|
||||
return "FixedFncTexture[7].T";
|
||||
case Attribute::FixedFncTexture7R:
|
||||
return "FixedFncTexture[7].R";
|
||||
case Attribute::FixedFncTexture7Q:
|
||||
return "FixedFncTexture[7].Q";
|
||||
case Attribute::FixedFncTexture8S:
|
||||
return "FixedFncTexture[8].S";
|
||||
case Attribute::FixedFncTexture8T:
|
||||
return "FixedFncTexture[8].T";
|
||||
case Attribute::FixedFncTexture8R:
|
||||
return "FixedFncTexture[8].R";
|
||||
case Attribute::FixedFncTexture8Q:
|
||||
return "FixedFncTexture[8].Q";
|
||||
case Attribute::FixedFncTexture9S:
|
||||
return "FixedFncTexture[9].S";
|
||||
case Attribute::FixedFncTexture9T:
|
||||
return "FixedFncTexture[9].T";
|
||||
case Attribute::FixedFncTexture9R:
|
||||
return "FixedFncTexture[9].R";
|
||||
case Attribute::FixedFncTexture9Q:
|
||||
return "FixedFncTexture[9].Q";
|
||||
case Attribute::ViewportMask:
|
||||
return "ViewportMask";
|
||||
case Attribute::FrontFace:
|
||||
return "FrontFace";
|
||||
}
|
||||
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Attribute attribute) noexcept {
|
||||
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
|
||||
}
|
||||
|
||||
u32 GenericAttributeIndex(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u;
|
||||
}
|
||||
|
||||
u32 GenericAttributeElement(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return static_cast<u32>(attribute) % 4;
|
||||
}
|
||||
|
||||
std::string NameOf(Attribute attribute) {
|
||||
switch (attribute) {
|
||||
case Attribute::PrimitiveId:
|
||||
return "PrimitiveId";
|
||||
case Attribute::Layer:
|
||||
return "Layer";
|
||||
case Attribute::ViewportIndex:
|
||||
return "ViewportIndex";
|
||||
case Attribute::PointSize:
|
||||
return "PointSize";
|
||||
case Attribute::PositionX:
|
||||
return "Position.X";
|
||||
case Attribute::PositionY:
|
||||
return "Position.Y";
|
||||
case Attribute::PositionZ:
|
||||
return "Position.Z";
|
||||
case Attribute::PositionW:
|
||||
return "Position.W";
|
||||
case Attribute::Generic0X:
|
||||
return "Generic[0].X";
|
||||
case Attribute::Generic0Y:
|
||||
return "Generic[0].Y";
|
||||
case Attribute::Generic0Z:
|
||||
return "Generic[0].Z";
|
||||
case Attribute::Generic0W:
|
||||
return "Generic[0].W";
|
||||
case Attribute::Generic1X:
|
||||
return "Generic[1].X";
|
||||
case Attribute::Generic1Y:
|
||||
return "Generic[1].Y";
|
||||
case Attribute::Generic1Z:
|
||||
return "Generic[1].Z";
|
||||
case Attribute::Generic1W:
|
||||
return "Generic[1].W";
|
||||
case Attribute::Generic2X:
|
||||
return "Generic[2].X";
|
||||
case Attribute::Generic2Y:
|
||||
return "Generic[2].Y";
|
||||
case Attribute::Generic2Z:
|
||||
return "Generic[2].Z";
|
||||
case Attribute::Generic2W:
|
||||
return "Generic[2].W";
|
||||
case Attribute::Generic3X:
|
||||
return "Generic[3].X";
|
||||
case Attribute::Generic3Y:
|
||||
return "Generic[3].Y";
|
||||
case Attribute::Generic3Z:
|
||||
return "Generic[3].Z";
|
||||
case Attribute::Generic3W:
|
||||
return "Generic[3].W";
|
||||
case Attribute::Generic4X:
|
||||
return "Generic[4].X";
|
||||
case Attribute::Generic4Y:
|
||||
return "Generic[4].Y";
|
||||
case Attribute::Generic4Z:
|
||||
return "Generic[4].Z";
|
||||
case Attribute::Generic4W:
|
||||
return "Generic[4].W";
|
||||
case Attribute::Generic5X:
|
||||
return "Generic[5].X";
|
||||
case Attribute::Generic5Y:
|
||||
return "Generic[5].Y";
|
||||
case Attribute::Generic5Z:
|
||||
return "Generic[5].Z";
|
||||
case Attribute::Generic5W:
|
||||
return "Generic[5].W";
|
||||
case Attribute::Generic6X:
|
||||
return "Generic[6].X";
|
||||
case Attribute::Generic6Y:
|
||||
return "Generic[6].Y";
|
||||
case Attribute::Generic6Z:
|
||||
return "Generic[6].Z";
|
||||
case Attribute::Generic6W:
|
||||
return "Generic[6].W";
|
||||
case Attribute::Generic7X:
|
||||
return "Generic[7].X";
|
||||
case Attribute::Generic7Y:
|
||||
return "Generic[7].Y";
|
||||
case Attribute::Generic7Z:
|
||||
return "Generic[7].Z";
|
||||
case Attribute::Generic7W:
|
||||
return "Generic[7].W";
|
||||
case Attribute::Generic8X:
|
||||
return "Generic[8].X";
|
||||
case Attribute::Generic8Y:
|
||||
return "Generic[8].Y";
|
||||
case Attribute::Generic8Z:
|
||||
return "Generic[8].Z";
|
||||
case Attribute::Generic8W:
|
||||
return "Generic[8].W";
|
||||
case Attribute::Generic9X:
|
||||
return "Generic[9].X";
|
||||
case Attribute::Generic9Y:
|
||||
return "Generic[9].Y";
|
||||
case Attribute::Generic9Z:
|
||||
return "Generic[9].Z";
|
||||
case Attribute::Generic9W:
|
||||
return "Generic[9].W";
|
||||
case Attribute::Generic10X:
|
||||
return "Generic[10].X";
|
||||
case Attribute::Generic10Y:
|
||||
return "Generic[10].Y";
|
||||
case Attribute::Generic10Z:
|
||||
return "Generic[10].Z";
|
||||
case Attribute::Generic10W:
|
||||
return "Generic[10].W";
|
||||
case Attribute::Generic11X:
|
||||
return "Generic[11].X";
|
||||
case Attribute::Generic11Y:
|
||||
return "Generic[11].Y";
|
||||
case Attribute::Generic11Z:
|
||||
return "Generic[11].Z";
|
||||
case Attribute::Generic11W:
|
||||
return "Generic[11].W";
|
||||
case Attribute::Generic12X:
|
||||
return "Generic[12].X";
|
||||
case Attribute::Generic12Y:
|
||||
return "Generic[12].Y";
|
||||
case Attribute::Generic12Z:
|
||||
return "Generic[12].Z";
|
||||
case Attribute::Generic12W:
|
||||
return "Generic[12].W";
|
||||
case Attribute::Generic13X:
|
||||
return "Generic[13].X";
|
||||
case Attribute::Generic13Y:
|
||||
return "Generic[13].Y";
|
||||
case Attribute::Generic13Z:
|
||||
return "Generic[13].Z";
|
||||
case Attribute::Generic13W:
|
||||
return "Generic[13].W";
|
||||
case Attribute::Generic14X:
|
||||
return "Generic[14].X";
|
||||
case Attribute::Generic14Y:
|
||||
return "Generic[14].Y";
|
||||
case Attribute::Generic14Z:
|
||||
return "Generic[14].Z";
|
||||
case Attribute::Generic14W:
|
||||
return "Generic[14].W";
|
||||
case Attribute::Generic15X:
|
||||
return "Generic[15].X";
|
||||
case Attribute::Generic15Y:
|
||||
return "Generic[15].Y";
|
||||
case Attribute::Generic15Z:
|
||||
return "Generic[15].Z";
|
||||
case Attribute::Generic15W:
|
||||
return "Generic[15].W";
|
||||
case Attribute::Generic16X:
|
||||
return "Generic[16].X";
|
||||
case Attribute::Generic16Y:
|
||||
return "Generic[16].Y";
|
||||
case Attribute::Generic16Z:
|
||||
return "Generic[16].Z";
|
||||
case Attribute::Generic16W:
|
||||
return "Generic[16].W";
|
||||
case Attribute::Generic17X:
|
||||
return "Generic[17].X";
|
||||
case Attribute::Generic17Y:
|
||||
return "Generic[17].Y";
|
||||
case Attribute::Generic17Z:
|
||||
return "Generic[17].Z";
|
||||
case Attribute::Generic17W:
|
||||
return "Generic[17].W";
|
||||
case Attribute::Generic18X:
|
||||
return "Generic[18].X";
|
||||
case Attribute::Generic18Y:
|
||||
return "Generic[18].Y";
|
||||
case Attribute::Generic18Z:
|
||||
return "Generic[18].Z";
|
||||
case Attribute::Generic18W:
|
||||
return "Generic[18].W";
|
||||
case Attribute::Generic19X:
|
||||
return "Generic[19].X";
|
||||
case Attribute::Generic19Y:
|
||||
return "Generic[19].Y";
|
||||
case Attribute::Generic19Z:
|
||||
return "Generic[19].Z";
|
||||
case Attribute::Generic19W:
|
||||
return "Generic[19].W";
|
||||
case Attribute::Generic20X:
|
||||
return "Generic[20].X";
|
||||
case Attribute::Generic20Y:
|
||||
return "Generic[20].Y";
|
||||
case Attribute::Generic20Z:
|
||||
return "Generic[20].Z";
|
||||
case Attribute::Generic20W:
|
||||
return "Generic[20].W";
|
||||
case Attribute::Generic21X:
|
||||
return "Generic[21].X";
|
||||
case Attribute::Generic21Y:
|
||||
return "Generic[21].Y";
|
||||
case Attribute::Generic21Z:
|
||||
return "Generic[21].Z";
|
||||
case Attribute::Generic21W:
|
||||
return "Generic[21].W";
|
||||
case Attribute::Generic22X:
|
||||
return "Generic[22].X";
|
||||
case Attribute::Generic22Y:
|
||||
return "Generic[22].Y";
|
||||
case Attribute::Generic22Z:
|
||||
return "Generic[22].Z";
|
||||
case Attribute::Generic22W:
|
||||
return "Generic[22].W";
|
||||
case Attribute::Generic23X:
|
||||
return "Generic[23].X";
|
||||
case Attribute::Generic23Y:
|
||||
return "Generic[23].Y";
|
||||
case Attribute::Generic23Z:
|
||||
return "Generic[23].Z";
|
||||
case Attribute::Generic23W:
|
||||
return "Generic[23].W";
|
||||
case Attribute::Generic24X:
|
||||
return "Generic[24].X";
|
||||
case Attribute::Generic24Y:
|
||||
return "Generic[24].Y";
|
||||
case Attribute::Generic24Z:
|
||||
return "Generic[24].Z";
|
||||
case Attribute::Generic24W:
|
||||
return "Generic[24].W";
|
||||
case Attribute::Generic25X:
|
||||
return "Generic[25].X";
|
||||
case Attribute::Generic25Y:
|
||||
return "Generic[25].Y";
|
||||
case Attribute::Generic25Z:
|
||||
return "Generic[25].Z";
|
||||
case Attribute::Generic25W:
|
||||
return "Generic[25].W";
|
||||
case Attribute::Generic26X:
|
||||
return "Generic[26].X";
|
||||
case Attribute::Generic26Y:
|
||||
return "Generic[26].Y";
|
||||
case Attribute::Generic26Z:
|
||||
return "Generic[26].Z";
|
||||
case Attribute::Generic26W:
|
||||
return "Generic[26].W";
|
||||
case Attribute::Generic27X:
|
||||
return "Generic[27].X";
|
||||
case Attribute::Generic27Y:
|
||||
return "Generic[27].Y";
|
||||
case Attribute::Generic27Z:
|
||||
return "Generic[27].Z";
|
||||
case Attribute::Generic27W:
|
||||
return "Generic[27].W";
|
||||
case Attribute::Generic28X:
|
||||
return "Generic[28].X";
|
||||
case Attribute::Generic28Y:
|
||||
return "Generic[28].Y";
|
||||
case Attribute::Generic28Z:
|
||||
return "Generic[28].Z";
|
||||
case Attribute::Generic28W:
|
||||
return "Generic[28].W";
|
||||
case Attribute::Generic29X:
|
||||
return "Generic[29].X";
|
||||
case Attribute::Generic29Y:
|
||||
return "Generic[29].Y";
|
||||
case Attribute::Generic29Z:
|
||||
return "Generic[29].Z";
|
||||
case Attribute::Generic29W:
|
||||
return "Generic[29].W";
|
||||
case Attribute::Generic30X:
|
||||
return "Generic[30].X";
|
||||
case Attribute::Generic30Y:
|
||||
return "Generic[30].Y";
|
||||
case Attribute::Generic30Z:
|
||||
return "Generic[30].Z";
|
||||
case Attribute::Generic30W:
|
||||
return "Generic[30].W";
|
||||
case Attribute::Generic31X:
|
||||
return "Generic[31].X";
|
||||
case Attribute::Generic31Y:
|
||||
return "Generic[31].Y";
|
||||
case Attribute::Generic31Z:
|
||||
return "Generic[31].Z";
|
||||
case Attribute::Generic31W:
|
||||
return "Generic[31].W";
|
||||
case Attribute::ColorFrontDiffuseR:
|
||||
return "ColorFrontDiffuse.R";
|
||||
case Attribute::ColorFrontDiffuseG:
|
||||
return "ColorFrontDiffuse.G";
|
||||
case Attribute::ColorFrontDiffuseB:
|
||||
return "ColorFrontDiffuse.B";
|
||||
case Attribute::ColorFrontDiffuseA:
|
||||
return "ColorFrontDiffuse.A";
|
||||
case Attribute::ColorFrontSpecularR:
|
||||
return "ColorFrontSpecular.R";
|
||||
case Attribute::ColorFrontSpecularG:
|
||||
return "ColorFrontSpecular.G";
|
||||
case Attribute::ColorFrontSpecularB:
|
||||
return "ColorFrontSpecular.B";
|
||||
case Attribute::ColorFrontSpecularA:
|
||||
return "ColorFrontSpecular.A";
|
||||
case Attribute::ColorBackDiffuseR:
|
||||
return "ColorBackDiffuse.R";
|
||||
case Attribute::ColorBackDiffuseG:
|
||||
return "ColorBackDiffuse.G";
|
||||
case Attribute::ColorBackDiffuseB:
|
||||
return "ColorBackDiffuse.B";
|
||||
case Attribute::ColorBackDiffuseA:
|
||||
return "ColorBackDiffuse.A";
|
||||
case Attribute::ColorBackSpecularR:
|
||||
return "ColorBackSpecular.R";
|
||||
case Attribute::ColorBackSpecularG:
|
||||
return "ColorBackSpecular.G";
|
||||
case Attribute::ColorBackSpecularB:
|
||||
return "ColorBackSpecular.B";
|
||||
case Attribute::ColorBackSpecularA:
|
||||
return "ColorBackSpecular.A";
|
||||
case Attribute::ClipDistance0:
|
||||
return "ClipDistance[0]";
|
||||
case Attribute::ClipDistance1:
|
||||
return "ClipDistance[1]";
|
||||
case Attribute::ClipDistance2:
|
||||
return "ClipDistance[2]";
|
||||
case Attribute::ClipDistance3:
|
||||
return "ClipDistance[3]";
|
||||
case Attribute::ClipDistance4:
|
||||
return "ClipDistance[4]";
|
||||
case Attribute::ClipDistance5:
|
||||
return "ClipDistance[5]";
|
||||
case Attribute::ClipDistance6:
|
||||
return "ClipDistance[6]";
|
||||
case Attribute::ClipDistance7:
|
||||
return "ClipDistance[7]";
|
||||
case Attribute::PointSpriteS:
|
||||
return "PointSprite.S";
|
||||
case Attribute::PointSpriteT:
|
||||
return "PointSprite.T";
|
||||
case Attribute::FogCoordinate:
|
||||
return "FogCoordinate";
|
||||
case Attribute::TessellationEvaluationPointU:
|
||||
return "TessellationEvaluationPoint.U";
|
||||
case Attribute::TessellationEvaluationPointV:
|
||||
return "TessellationEvaluationPoint.V";
|
||||
case Attribute::InstanceId:
|
||||
return "InstanceId";
|
||||
case Attribute::VertexId:
|
||||
return "VertexId";
|
||||
case Attribute::FixedFncTexture0S:
|
||||
return "FixedFncTexture[0].S";
|
||||
case Attribute::FixedFncTexture0T:
|
||||
return "FixedFncTexture[0].T";
|
||||
case Attribute::FixedFncTexture0R:
|
||||
return "FixedFncTexture[0].R";
|
||||
case Attribute::FixedFncTexture0Q:
|
||||
return "FixedFncTexture[0].Q";
|
||||
case Attribute::FixedFncTexture1S:
|
||||
return "FixedFncTexture[1].S";
|
||||
case Attribute::FixedFncTexture1T:
|
||||
return "FixedFncTexture[1].T";
|
||||
case Attribute::FixedFncTexture1R:
|
||||
return "FixedFncTexture[1].R";
|
||||
case Attribute::FixedFncTexture1Q:
|
||||
return "FixedFncTexture[1].Q";
|
||||
case Attribute::FixedFncTexture2S:
|
||||
return "FixedFncTexture[2].S";
|
||||
case Attribute::FixedFncTexture2T:
|
||||
return "FixedFncTexture[2].T";
|
||||
case Attribute::FixedFncTexture2R:
|
||||
return "FixedFncTexture[2].R";
|
||||
case Attribute::FixedFncTexture2Q:
|
||||
return "FixedFncTexture[2].Q";
|
||||
case Attribute::FixedFncTexture3S:
|
||||
return "FixedFncTexture[3].S";
|
||||
case Attribute::FixedFncTexture3T:
|
||||
return "FixedFncTexture[3].T";
|
||||
case Attribute::FixedFncTexture3R:
|
||||
return "FixedFncTexture[3].R";
|
||||
case Attribute::FixedFncTexture3Q:
|
||||
return "FixedFncTexture[3].Q";
|
||||
case Attribute::FixedFncTexture4S:
|
||||
return "FixedFncTexture[4].S";
|
||||
case Attribute::FixedFncTexture4T:
|
||||
return "FixedFncTexture[4].T";
|
||||
case Attribute::FixedFncTexture4R:
|
||||
return "FixedFncTexture[4].R";
|
||||
case Attribute::FixedFncTexture4Q:
|
||||
return "FixedFncTexture[4].Q";
|
||||
case Attribute::FixedFncTexture5S:
|
||||
return "FixedFncTexture[5].S";
|
||||
case Attribute::FixedFncTexture5T:
|
||||
return "FixedFncTexture[5].T";
|
||||
case Attribute::FixedFncTexture5R:
|
||||
return "FixedFncTexture[5].R";
|
||||
case Attribute::FixedFncTexture5Q:
|
||||
return "FixedFncTexture[5].Q";
|
||||
case Attribute::FixedFncTexture6S:
|
||||
return "FixedFncTexture[6].S";
|
||||
case Attribute::FixedFncTexture6T:
|
||||
return "FixedFncTexture[6].T";
|
||||
case Attribute::FixedFncTexture6R:
|
||||
return "FixedFncTexture[6].R";
|
||||
case Attribute::FixedFncTexture6Q:
|
||||
return "FixedFncTexture[6].Q";
|
||||
case Attribute::FixedFncTexture7S:
|
||||
return "FixedFncTexture[7].S";
|
||||
case Attribute::FixedFncTexture7T:
|
||||
return "FixedFncTexture[7].T";
|
||||
case Attribute::FixedFncTexture7R:
|
||||
return "FixedFncTexture[7].R";
|
||||
case Attribute::FixedFncTexture7Q:
|
||||
return "FixedFncTexture[7].Q";
|
||||
case Attribute::FixedFncTexture8S:
|
||||
return "FixedFncTexture[8].S";
|
||||
case Attribute::FixedFncTexture8T:
|
||||
return "FixedFncTexture[8].T";
|
||||
case Attribute::FixedFncTexture8R:
|
||||
return "FixedFncTexture[8].R";
|
||||
case Attribute::FixedFncTexture8Q:
|
||||
return "FixedFncTexture[8].Q";
|
||||
case Attribute::FixedFncTexture9S:
|
||||
return "FixedFncTexture[9].S";
|
||||
case Attribute::FixedFncTexture9T:
|
||||
return "FixedFncTexture[9].T";
|
||||
case Attribute::FixedFncTexture9R:
|
||||
return "FixedFncTexture[9].R";
|
||||
case Attribute::FixedFncTexture9Q:
|
||||
return "FixedFncTexture[9].Q";
|
||||
case Attribute::ViewportMask:
|
||||
return "ViewportMask";
|
||||
case Attribute::FrontFace:
|
||||
return "FrontFace";
|
||||
}
|
||||
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,251 +1,251 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Attribute : u64 {
|
||||
PrimitiveId = 24,
|
||||
Layer = 25,
|
||||
ViewportIndex = 26,
|
||||
PointSize = 27,
|
||||
PositionX = 28,
|
||||
PositionY = 29,
|
||||
PositionZ = 30,
|
||||
PositionW = 31,
|
||||
Generic0X = 32,
|
||||
Generic0Y = 33,
|
||||
Generic0Z = 34,
|
||||
Generic0W = 35,
|
||||
Generic1X = 36,
|
||||
Generic1Y = 37,
|
||||
Generic1Z = 38,
|
||||
Generic1W = 39,
|
||||
Generic2X = 40,
|
||||
Generic2Y = 41,
|
||||
Generic2Z = 42,
|
||||
Generic2W = 43,
|
||||
Generic3X = 44,
|
||||
Generic3Y = 45,
|
||||
Generic3Z = 46,
|
||||
Generic3W = 47,
|
||||
Generic4X = 48,
|
||||
Generic4Y = 49,
|
||||
Generic4Z = 50,
|
||||
Generic4W = 51,
|
||||
Generic5X = 52,
|
||||
Generic5Y = 53,
|
||||
Generic5Z = 54,
|
||||
Generic5W = 55,
|
||||
Generic6X = 56,
|
||||
Generic6Y = 57,
|
||||
Generic6Z = 58,
|
||||
Generic6W = 59,
|
||||
Generic7X = 60,
|
||||
Generic7Y = 61,
|
||||
Generic7Z = 62,
|
||||
Generic7W = 63,
|
||||
Generic8X = 64,
|
||||
Generic8Y = 65,
|
||||
Generic8Z = 66,
|
||||
Generic8W = 67,
|
||||
Generic9X = 68,
|
||||
Generic9Y = 69,
|
||||
Generic9Z = 70,
|
||||
Generic9W = 71,
|
||||
Generic10X = 72,
|
||||
Generic10Y = 73,
|
||||
Generic10Z = 74,
|
||||
Generic10W = 75,
|
||||
Generic11X = 76,
|
||||
Generic11Y = 77,
|
||||
Generic11Z = 78,
|
||||
Generic11W = 79,
|
||||
Generic12X = 80,
|
||||
Generic12Y = 81,
|
||||
Generic12Z = 82,
|
||||
Generic12W = 83,
|
||||
Generic13X = 84,
|
||||
Generic13Y = 85,
|
||||
Generic13Z = 86,
|
||||
Generic13W = 87,
|
||||
Generic14X = 88,
|
||||
Generic14Y = 89,
|
||||
Generic14Z = 90,
|
||||
Generic14W = 91,
|
||||
Generic15X = 92,
|
||||
Generic15Y = 93,
|
||||
Generic15Z = 94,
|
||||
Generic15W = 95,
|
||||
Generic16X = 96,
|
||||
Generic16Y = 97,
|
||||
Generic16Z = 98,
|
||||
Generic16W = 99,
|
||||
Generic17X = 100,
|
||||
Generic17Y = 101,
|
||||
Generic17Z = 102,
|
||||
Generic17W = 103,
|
||||
Generic18X = 104,
|
||||
Generic18Y = 105,
|
||||
Generic18Z = 106,
|
||||
Generic18W = 107,
|
||||
Generic19X = 108,
|
||||
Generic19Y = 109,
|
||||
Generic19Z = 110,
|
||||
Generic19W = 111,
|
||||
Generic20X = 112,
|
||||
Generic20Y = 113,
|
||||
Generic20Z = 114,
|
||||
Generic20W = 115,
|
||||
Generic21X = 116,
|
||||
Generic21Y = 117,
|
||||
Generic21Z = 118,
|
||||
Generic21W = 119,
|
||||
Generic22X = 120,
|
||||
Generic22Y = 121,
|
||||
Generic22Z = 122,
|
||||
Generic22W = 123,
|
||||
Generic23X = 124,
|
||||
Generic23Y = 125,
|
||||
Generic23Z = 126,
|
||||
Generic23W = 127,
|
||||
Generic24X = 128,
|
||||
Generic24Y = 129,
|
||||
Generic24Z = 130,
|
||||
Generic24W = 131,
|
||||
Generic25X = 132,
|
||||
Generic25Y = 133,
|
||||
Generic25Z = 134,
|
||||
Generic25W = 135,
|
||||
Generic26X = 136,
|
||||
Generic26Y = 137,
|
||||
Generic26Z = 138,
|
||||
Generic26W = 139,
|
||||
Generic27X = 140,
|
||||
Generic27Y = 141,
|
||||
Generic27Z = 142,
|
||||
Generic27W = 143,
|
||||
Generic28X = 144,
|
||||
Generic28Y = 145,
|
||||
Generic28Z = 146,
|
||||
Generic28W = 147,
|
||||
Generic29X = 148,
|
||||
Generic29Y = 149,
|
||||
Generic29Z = 150,
|
||||
Generic29W = 151,
|
||||
Generic30X = 152,
|
||||
Generic30Y = 153,
|
||||
Generic30Z = 154,
|
||||
Generic30W = 155,
|
||||
Generic31X = 156,
|
||||
Generic31Y = 157,
|
||||
Generic31Z = 158,
|
||||
Generic31W = 159,
|
||||
ColorFrontDiffuseR = 160,
|
||||
ColorFrontDiffuseG = 161,
|
||||
ColorFrontDiffuseB = 162,
|
||||
ColorFrontDiffuseA = 163,
|
||||
ColorFrontSpecularR = 164,
|
||||
ColorFrontSpecularG = 165,
|
||||
ColorFrontSpecularB = 166,
|
||||
ColorFrontSpecularA = 167,
|
||||
ColorBackDiffuseR = 168,
|
||||
ColorBackDiffuseG = 169,
|
||||
ColorBackDiffuseB = 170,
|
||||
ColorBackDiffuseA = 171,
|
||||
ColorBackSpecularR = 172,
|
||||
ColorBackSpecularG = 173,
|
||||
ColorBackSpecularB = 174,
|
||||
ColorBackSpecularA = 175,
|
||||
ClipDistance0 = 176,
|
||||
ClipDistance1 = 177,
|
||||
ClipDistance2 = 178,
|
||||
ClipDistance3 = 179,
|
||||
ClipDistance4 = 180,
|
||||
ClipDistance5 = 181,
|
||||
ClipDistance6 = 182,
|
||||
ClipDistance7 = 183,
|
||||
PointSpriteS = 184,
|
||||
PointSpriteT = 185,
|
||||
FogCoordinate = 186,
|
||||
TessellationEvaluationPointU = 188,
|
||||
TessellationEvaluationPointV = 189,
|
||||
InstanceId = 190,
|
||||
VertexId = 191,
|
||||
FixedFncTexture0S = 192,
|
||||
FixedFncTexture0T = 193,
|
||||
FixedFncTexture0R = 194,
|
||||
FixedFncTexture0Q = 195,
|
||||
FixedFncTexture1S = 196,
|
||||
FixedFncTexture1T = 197,
|
||||
FixedFncTexture1R = 198,
|
||||
FixedFncTexture1Q = 199,
|
||||
FixedFncTexture2S = 200,
|
||||
FixedFncTexture2T = 201,
|
||||
FixedFncTexture2R = 202,
|
||||
FixedFncTexture2Q = 203,
|
||||
FixedFncTexture3S = 204,
|
||||
FixedFncTexture3T = 205,
|
||||
FixedFncTexture3R = 206,
|
||||
FixedFncTexture3Q = 207,
|
||||
FixedFncTexture4S = 208,
|
||||
FixedFncTexture4T = 209,
|
||||
FixedFncTexture4R = 210,
|
||||
FixedFncTexture4Q = 211,
|
||||
FixedFncTexture5S = 212,
|
||||
FixedFncTexture5T = 213,
|
||||
FixedFncTexture5R = 214,
|
||||
FixedFncTexture5Q = 215,
|
||||
FixedFncTexture6S = 216,
|
||||
FixedFncTexture6T = 217,
|
||||
FixedFncTexture6R = 218,
|
||||
FixedFncTexture6Q = 219,
|
||||
FixedFncTexture7S = 220,
|
||||
FixedFncTexture7T = 221,
|
||||
FixedFncTexture7R = 222,
|
||||
FixedFncTexture7Q = 223,
|
||||
FixedFncTexture8S = 224,
|
||||
FixedFncTexture8T = 225,
|
||||
FixedFncTexture8R = 226,
|
||||
FixedFncTexture8Q = 227,
|
||||
FixedFncTexture9S = 228,
|
||||
FixedFncTexture9T = 229,
|
||||
FixedFncTexture9R = 230,
|
||||
FixedFncTexture9Q = 231,
|
||||
ViewportMask = 232,
|
||||
FrontFace = 255,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeElement(Attribute attribute);
|
||||
|
||||
[[nodiscard]] std::string NameOf(Attribute attribute);
|
||||
|
||||
[[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept {
|
||||
return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Attribute> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute));
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Attribute : u64 {
|
||||
PrimitiveId = 24,
|
||||
Layer = 25,
|
||||
ViewportIndex = 26,
|
||||
PointSize = 27,
|
||||
PositionX = 28,
|
||||
PositionY = 29,
|
||||
PositionZ = 30,
|
||||
PositionW = 31,
|
||||
Generic0X = 32,
|
||||
Generic0Y = 33,
|
||||
Generic0Z = 34,
|
||||
Generic0W = 35,
|
||||
Generic1X = 36,
|
||||
Generic1Y = 37,
|
||||
Generic1Z = 38,
|
||||
Generic1W = 39,
|
||||
Generic2X = 40,
|
||||
Generic2Y = 41,
|
||||
Generic2Z = 42,
|
||||
Generic2W = 43,
|
||||
Generic3X = 44,
|
||||
Generic3Y = 45,
|
||||
Generic3Z = 46,
|
||||
Generic3W = 47,
|
||||
Generic4X = 48,
|
||||
Generic4Y = 49,
|
||||
Generic4Z = 50,
|
||||
Generic4W = 51,
|
||||
Generic5X = 52,
|
||||
Generic5Y = 53,
|
||||
Generic5Z = 54,
|
||||
Generic5W = 55,
|
||||
Generic6X = 56,
|
||||
Generic6Y = 57,
|
||||
Generic6Z = 58,
|
||||
Generic6W = 59,
|
||||
Generic7X = 60,
|
||||
Generic7Y = 61,
|
||||
Generic7Z = 62,
|
||||
Generic7W = 63,
|
||||
Generic8X = 64,
|
||||
Generic8Y = 65,
|
||||
Generic8Z = 66,
|
||||
Generic8W = 67,
|
||||
Generic9X = 68,
|
||||
Generic9Y = 69,
|
||||
Generic9Z = 70,
|
||||
Generic9W = 71,
|
||||
Generic10X = 72,
|
||||
Generic10Y = 73,
|
||||
Generic10Z = 74,
|
||||
Generic10W = 75,
|
||||
Generic11X = 76,
|
||||
Generic11Y = 77,
|
||||
Generic11Z = 78,
|
||||
Generic11W = 79,
|
||||
Generic12X = 80,
|
||||
Generic12Y = 81,
|
||||
Generic12Z = 82,
|
||||
Generic12W = 83,
|
||||
Generic13X = 84,
|
||||
Generic13Y = 85,
|
||||
Generic13Z = 86,
|
||||
Generic13W = 87,
|
||||
Generic14X = 88,
|
||||
Generic14Y = 89,
|
||||
Generic14Z = 90,
|
||||
Generic14W = 91,
|
||||
Generic15X = 92,
|
||||
Generic15Y = 93,
|
||||
Generic15Z = 94,
|
||||
Generic15W = 95,
|
||||
Generic16X = 96,
|
||||
Generic16Y = 97,
|
||||
Generic16Z = 98,
|
||||
Generic16W = 99,
|
||||
Generic17X = 100,
|
||||
Generic17Y = 101,
|
||||
Generic17Z = 102,
|
||||
Generic17W = 103,
|
||||
Generic18X = 104,
|
||||
Generic18Y = 105,
|
||||
Generic18Z = 106,
|
||||
Generic18W = 107,
|
||||
Generic19X = 108,
|
||||
Generic19Y = 109,
|
||||
Generic19Z = 110,
|
||||
Generic19W = 111,
|
||||
Generic20X = 112,
|
||||
Generic20Y = 113,
|
||||
Generic20Z = 114,
|
||||
Generic20W = 115,
|
||||
Generic21X = 116,
|
||||
Generic21Y = 117,
|
||||
Generic21Z = 118,
|
||||
Generic21W = 119,
|
||||
Generic22X = 120,
|
||||
Generic22Y = 121,
|
||||
Generic22Z = 122,
|
||||
Generic22W = 123,
|
||||
Generic23X = 124,
|
||||
Generic23Y = 125,
|
||||
Generic23Z = 126,
|
||||
Generic23W = 127,
|
||||
Generic24X = 128,
|
||||
Generic24Y = 129,
|
||||
Generic24Z = 130,
|
||||
Generic24W = 131,
|
||||
Generic25X = 132,
|
||||
Generic25Y = 133,
|
||||
Generic25Z = 134,
|
||||
Generic25W = 135,
|
||||
Generic26X = 136,
|
||||
Generic26Y = 137,
|
||||
Generic26Z = 138,
|
||||
Generic26W = 139,
|
||||
Generic27X = 140,
|
||||
Generic27Y = 141,
|
||||
Generic27Z = 142,
|
||||
Generic27W = 143,
|
||||
Generic28X = 144,
|
||||
Generic28Y = 145,
|
||||
Generic28Z = 146,
|
||||
Generic28W = 147,
|
||||
Generic29X = 148,
|
||||
Generic29Y = 149,
|
||||
Generic29Z = 150,
|
||||
Generic29W = 151,
|
||||
Generic30X = 152,
|
||||
Generic30Y = 153,
|
||||
Generic30Z = 154,
|
||||
Generic30W = 155,
|
||||
Generic31X = 156,
|
||||
Generic31Y = 157,
|
||||
Generic31Z = 158,
|
||||
Generic31W = 159,
|
||||
ColorFrontDiffuseR = 160,
|
||||
ColorFrontDiffuseG = 161,
|
||||
ColorFrontDiffuseB = 162,
|
||||
ColorFrontDiffuseA = 163,
|
||||
ColorFrontSpecularR = 164,
|
||||
ColorFrontSpecularG = 165,
|
||||
ColorFrontSpecularB = 166,
|
||||
ColorFrontSpecularA = 167,
|
||||
ColorBackDiffuseR = 168,
|
||||
ColorBackDiffuseG = 169,
|
||||
ColorBackDiffuseB = 170,
|
||||
ColorBackDiffuseA = 171,
|
||||
ColorBackSpecularR = 172,
|
||||
ColorBackSpecularG = 173,
|
||||
ColorBackSpecularB = 174,
|
||||
ColorBackSpecularA = 175,
|
||||
ClipDistance0 = 176,
|
||||
ClipDistance1 = 177,
|
||||
ClipDistance2 = 178,
|
||||
ClipDistance3 = 179,
|
||||
ClipDistance4 = 180,
|
||||
ClipDistance5 = 181,
|
||||
ClipDistance6 = 182,
|
||||
ClipDistance7 = 183,
|
||||
PointSpriteS = 184,
|
||||
PointSpriteT = 185,
|
||||
FogCoordinate = 186,
|
||||
TessellationEvaluationPointU = 188,
|
||||
TessellationEvaluationPointV = 189,
|
||||
InstanceId = 190,
|
||||
VertexId = 191,
|
||||
FixedFncTexture0S = 192,
|
||||
FixedFncTexture0T = 193,
|
||||
FixedFncTexture0R = 194,
|
||||
FixedFncTexture0Q = 195,
|
||||
FixedFncTexture1S = 196,
|
||||
FixedFncTexture1T = 197,
|
||||
FixedFncTexture1R = 198,
|
||||
FixedFncTexture1Q = 199,
|
||||
FixedFncTexture2S = 200,
|
||||
FixedFncTexture2T = 201,
|
||||
FixedFncTexture2R = 202,
|
||||
FixedFncTexture2Q = 203,
|
||||
FixedFncTexture3S = 204,
|
||||
FixedFncTexture3T = 205,
|
||||
FixedFncTexture3R = 206,
|
||||
FixedFncTexture3Q = 207,
|
||||
FixedFncTexture4S = 208,
|
||||
FixedFncTexture4T = 209,
|
||||
FixedFncTexture4R = 210,
|
||||
FixedFncTexture4Q = 211,
|
||||
FixedFncTexture5S = 212,
|
||||
FixedFncTexture5T = 213,
|
||||
FixedFncTexture5R = 214,
|
||||
FixedFncTexture5Q = 215,
|
||||
FixedFncTexture6S = 216,
|
||||
FixedFncTexture6T = 217,
|
||||
FixedFncTexture6R = 218,
|
||||
FixedFncTexture6Q = 219,
|
||||
FixedFncTexture7S = 220,
|
||||
FixedFncTexture7T = 221,
|
||||
FixedFncTexture7R = 222,
|
||||
FixedFncTexture7Q = 223,
|
||||
FixedFncTexture8S = 224,
|
||||
FixedFncTexture8T = 225,
|
||||
FixedFncTexture8R = 226,
|
||||
FixedFncTexture8Q = 227,
|
||||
FixedFncTexture9S = 228,
|
||||
FixedFncTexture9T = 229,
|
||||
FixedFncTexture9R = 230,
|
||||
FixedFncTexture9Q = 231,
|
||||
ViewportMask = 232,
|
||||
FrontFace = 255,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeElement(Attribute attribute);
|
||||
|
||||
[[nodiscard]] std::string NameOf(Attribute attribute);
|
||||
|
||||
[[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept {
|
||||
return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Attribute> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,151 +1,151 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
|
||||
Block::~Block() = default;
|
||||
|
||||
void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||
PrependNewInst(end(), op, args);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
||||
Inst* const inst{inst_pool->Create(base_inst)};
|
||||
return instructions.insert(insertion_point, *inst);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args, u32 flags) {
|
||||
Inst* const inst{inst_pool->Create(op, flags)};
|
||||
const auto result_it{instructions.insert(insertion_point, *inst)};
|
||||
|
||||
if (inst->NumArgs() != args.size()) {
|
||||
throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
|
||||
}
|
||||
std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
|
||||
inst->SetArg(index, arg);
|
||||
++index;
|
||||
});
|
||||
return result_it;
|
||||
}
|
||||
|
||||
void Block::AddBranch(Block* block) {
|
||||
if (std::ranges::find(imm_successors, block) != imm_successors.end()) {
|
||||
throw LogicError("Successor already inserted");
|
||||
}
|
||||
if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) {
|
||||
throw LogicError("Predecessor already inserted");
|
||||
}
|
||||
imm_successors.push_back(block);
|
||||
block->imm_predecessors.push_back(this);
|
||||
}
|
||||
|
||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
||||
Block* block) {
|
||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
|
||||
return fmt::format("{{Block ${}}}", it->second);
|
||||
}
|
||||
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
|
||||
}
|
||||
|
||||
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Inst* inst) {
|
||||
const auto [it, is_inserted]{inst_to_index.emplace(inst, inst_index + 1)};
|
||||
if (is_inserted) {
|
||||
++inst_index;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Value& arg) {
|
||||
if (arg.IsEmpty()) {
|
||||
return "<null>";
|
||||
}
|
||||
if (!arg.IsImmediate() || arg.IsIdentity()) {
|
||||
return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
|
||||
}
|
||||
switch (arg.Type()) {
|
||||
case Type::U1:
|
||||
return fmt::format("#{}", arg.U1() ? "true" : "false");
|
||||
case Type::U8:
|
||||
return fmt::format("#{}", arg.U8());
|
||||
case Type::U16:
|
||||
return fmt::format("#{}", arg.U16());
|
||||
case Type::U32:
|
||||
return fmt::format("#{}", arg.U32());
|
||||
case Type::U64:
|
||||
return fmt::format("#{}", arg.U64());
|
||||
case Type::F32:
|
||||
return fmt::format("#{}", arg.F32());
|
||||
case Type::Reg:
|
||||
return fmt::format("{}", arg.Reg());
|
||||
case Type::Pred:
|
||||
return fmt::format("{}", arg.Pred());
|
||||
case Type::Attribute:
|
||||
return fmt::format("{}", arg.Attribute());
|
||||
default:
|
||||
return "<unknown immediate type>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block) {
|
||||
size_t inst_index{0};
|
||||
std::map<const Inst*, size_t> inst_to_index;
|
||||
return DumpBlock(block, {}, inst_to_index, inst_index);
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
|
||||
std::string ret{"Block"};
|
||||
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
|
||||
ret += fmt::format(" ${}", it->second);
|
||||
}
|
||||
ret += '\n';
|
||||
for (const Inst& inst : block) {
|
||||
const Opcode op{inst.GetOpcode()};
|
||||
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format("%{:<5} = {}", InstIndex(inst_to_index, inst_index, &inst), op);
|
||||
} else {
|
||||
ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
|
||||
}
|
||||
const size_t arg_count{inst.NumArgs()};
|
||||
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
|
||||
const Value arg{inst.Arg(arg_index)};
|
||||
const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)};
|
||||
ret += arg_index != 0 ? ", " : " ";
|
||||
if (op == Opcode::Phi) {
|
||||
ret += fmt::format("[ {}, {} ]", arg_str,
|
||||
BlockToIndex(block_to_index, inst.PhiBlock(arg_index)));
|
||||
} else {
|
||||
ret += arg_str;
|
||||
}
|
||||
if (op != Opcode::Phi) {
|
||||
const Type actual_type{arg.Type()};
|
||||
const Type expected_type{ArgTypeOf(op, arg_index)};
|
||||
if (!AreTypesCompatible(actual_type, expected_type)) {
|
||||
ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format(" (uses: {})\n", inst.UseCount());
|
||||
} else {
|
||||
ret += '\n';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
|
||||
Block::~Block() = default;
|
||||
|
||||
void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||
PrependNewInst(end(), op, args);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
||||
Inst* const inst{inst_pool->Create(base_inst)};
|
||||
return instructions.insert(insertion_point, *inst);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args, u32 flags) {
|
||||
Inst* const inst{inst_pool->Create(op, flags)};
|
||||
const auto result_it{instructions.insert(insertion_point, *inst)};
|
||||
|
||||
if (inst->NumArgs() != args.size()) {
|
||||
throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
|
||||
}
|
||||
std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
|
||||
inst->SetArg(index, arg);
|
||||
++index;
|
||||
});
|
||||
return result_it;
|
||||
}
|
||||
|
||||
void Block::AddBranch(Block* block) {
|
||||
if (std::ranges::find(imm_successors, block) != imm_successors.end()) {
|
||||
throw LogicError("Successor already inserted");
|
||||
}
|
||||
if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) {
|
||||
throw LogicError("Predecessor already inserted");
|
||||
}
|
||||
imm_successors.push_back(block);
|
||||
block->imm_predecessors.push_back(this);
|
||||
}
|
||||
|
||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
||||
Block* block) {
|
||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
|
||||
return fmt::format("{{Block ${}}}", it->second);
|
||||
}
|
||||
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
|
||||
}
|
||||
|
||||
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Inst* inst) {
|
||||
const auto [it, is_inserted]{inst_to_index.emplace(inst, inst_index + 1)};
|
||||
if (is_inserted) {
|
||||
++inst_index;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Value& arg) {
|
||||
if (arg.IsEmpty()) {
|
||||
return "<null>";
|
||||
}
|
||||
if (!arg.IsImmediate() || arg.IsIdentity()) {
|
||||
return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
|
||||
}
|
||||
switch (arg.Type()) {
|
||||
case Type::U1:
|
||||
return fmt::format("#{}", arg.U1() ? "true" : "false");
|
||||
case Type::U8:
|
||||
return fmt::format("#{}", arg.U8());
|
||||
case Type::U16:
|
||||
return fmt::format("#{}", arg.U16());
|
||||
case Type::U32:
|
||||
return fmt::format("#{}", arg.U32());
|
||||
case Type::U64:
|
||||
return fmt::format("#{}", arg.U64());
|
||||
case Type::F32:
|
||||
return fmt::format("#{}", arg.F32());
|
||||
case Type::Reg:
|
||||
return fmt::format("{}", arg.Reg());
|
||||
case Type::Pred:
|
||||
return fmt::format("{}", arg.Pred());
|
||||
case Type::Attribute:
|
||||
return fmt::format("{}", arg.Attribute());
|
||||
default:
|
||||
return "<unknown immediate type>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block) {
|
||||
size_t inst_index{0};
|
||||
std::map<const Inst*, size_t> inst_to_index;
|
||||
return DumpBlock(block, {}, inst_to_index, inst_index);
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
|
||||
std::string ret{"Block"};
|
||||
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
|
||||
ret += fmt::format(" ${}", it->second);
|
||||
}
|
||||
ret += '\n';
|
||||
for (const Inst& inst : block) {
|
||||
const Opcode op{inst.GetOpcode()};
|
||||
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format("%{:<5} = {}", InstIndex(inst_to_index, inst_index, &inst), op);
|
||||
} else {
|
||||
ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
|
||||
}
|
||||
const size_t arg_count{inst.NumArgs()};
|
||||
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
|
||||
const Value arg{inst.Arg(arg_index)};
|
||||
const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)};
|
||||
ret += arg_index != 0 ? ", " : " ";
|
||||
if (op == Opcode::Phi) {
|
||||
ret += fmt::format("[ {}, {} ]", arg_str,
|
||||
BlockToIndex(block_to_index, inst.PhiBlock(arg_index)));
|
||||
} else {
|
||||
ret += arg_str;
|
||||
}
|
||||
if (op != Opcode::Phi) {
|
||||
const Type actual_type{arg.Type()};
|
||||
const Type expected_type{ArgTypeOf(op, arg_index)};
|
||||
if (!AreTypesCompatible(actual_type, expected_type)) {
|
||||
ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format(" (uses: {})\n", inst.UseCount());
|
||||
} else {
|
||||
ret += '\n';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,201 +1,201 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block {
|
||||
public:
|
||||
using InstructionList = boost::intrusive::list<Inst>;
|
||||
using size_type = InstructionList::size_type;
|
||||
using iterator = InstructionList::iterator;
|
||||
using const_iterator = InstructionList::const_iterator;
|
||||
using reverse_iterator = InstructionList::reverse_iterator;
|
||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||
|
||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
||||
~Block();
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
|
||||
Block(Block&&) = default;
|
||||
Block& operator=(Block&&) = default;
|
||||
|
||||
/// Appends a new instruction to the end of this basic block.
|
||||
void AppendNewInst(Opcode op, std::initializer_list<Value> args);
|
||||
|
||||
/// Prepends a copy of an instruction to this basic block before the insertion point.
|
||||
iterator PrependNewInst(iterator insertion_point, const Inst& base_inst);
|
||||
|
||||
/// Prepends a new instruction to this basic block before the insertion point.
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args = {}, u32 flags = 0);
|
||||
|
||||
/// Adds a new branch to this basic block.
|
||||
void AddBranch(Block* block);
|
||||
|
||||
/// Gets a mutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] InstructionList& Instructions() noexcept {
|
||||
return instructions;
|
||||
}
|
||||
/// Gets an immutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] const InstructionList& Instructions() const noexcept {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/// Gets an immutable span to the immediate predecessors.
|
||||
[[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept {
|
||||
return imm_predecessors;
|
||||
}
|
||||
/// Gets an immutable span to the immediate successors.
|
||||
[[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept {
|
||||
return imm_successors;
|
||||
}
|
||||
|
||||
/// Intrusively store the host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
void SetDefinition(DefinitionType def) {
|
||||
definition = Common::BitCast<u32>(def);
|
||||
}
|
||||
|
||||
/// Return the intrusively stored host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
[[nodiscard]] DefinitionType Definition() const noexcept {
|
||||
return Common::BitCast<DefinitionType>(definition);
|
||||
}
|
||||
|
||||
void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept {
|
||||
ssa_reg_values[RegIndex(reg)] = value;
|
||||
}
|
||||
const Value& SsaRegValue(IR::Reg reg) const noexcept {
|
||||
return ssa_reg_values[RegIndex(reg)];
|
||||
}
|
||||
|
||||
void SsaSeal() noexcept {
|
||||
is_ssa_sealed = true;
|
||||
}
|
||||
[[nodiscard]] bool IsSsaSealed() const noexcept {
|
||||
return is_ssa_sealed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return instructions.empty();
|
||||
}
|
||||
[[nodiscard]] size_type size() const {
|
||||
return instructions.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& front() {
|
||||
return instructions.front();
|
||||
}
|
||||
[[nodiscard]] const Inst& front() const {
|
||||
return instructions.front();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& back() {
|
||||
return instructions.back();
|
||||
}
|
||||
[[nodiscard]] const Inst& back() const {
|
||||
return instructions.back();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] const_iterator begin() const {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] iterator end() {
|
||||
return instructions.end();
|
||||
}
|
||||
[[nodiscard]] const_iterator end() const {
|
||||
return instructions.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rbegin() {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] reverse_iterator rend() {
|
||||
return instructions.rend();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rend() const {
|
||||
return instructions.rend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cbegin() const {
|
||||
return instructions.cbegin();
|
||||
}
|
||||
[[nodiscard]] const_iterator cend() const {
|
||||
return instructions.cend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const {
|
||||
return instructions.crbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator crend() const {
|
||||
return instructions.crend();
|
||||
}
|
||||
|
||||
// Set the order of the block, it can be set pre order, the user decides
|
||||
void SetOrder(u32 new_order) {
|
||||
order = new_order;
|
||||
}
|
||||
|
||||
// Get the order of the block.
|
||||
// The higher, the closer is the block to the end.
|
||||
[[nodiscard]] u32 GetOrder() const {
|
||||
return order;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Memory pool for instruction list
|
||||
ObjectPool<Inst>* inst_pool;
|
||||
|
||||
/// List of instructions in this block
|
||||
InstructionList instructions;
|
||||
|
||||
/// Block immediate predecessors
|
||||
std::vector<Block*> imm_predecessors;
|
||||
/// Block immediate successors
|
||||
std::vector<Block*> imm_successors;
|
||||
|
||||
/// Intrusively store the value of a register in the block.
|
||||
std::array<Value, NUM_REGS> ssa_reg_values;
|
||||
/// Intrusively store if the block is sealed in the SSA pass.
|
||||
bool is_ssa_sealed{false};
|
||||
|
||||
/// Intrusively stored host definition of this block.
|
||||
u32 definition{};
|
||||
|
||||
/// Order of the block.
|
||||
u32 order{};
|
||||
};
|
||||
|
||||
using BlockList = std::vector<Block*>;
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block);
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block,
|
||||
const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index,
|
||||
size_t& inst_index);
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block {
|
||||
public:
|
||||
using InstructionList = boost::intrusive::list<Inst>;
|
||||
using size_type = InstructionList::size_type;
|
||||
using iterator = InstructionList::iterator;
|
||||
using const_iterator = InstructionList::const_iterator;
|
||||
using reverse_iterator = InstructionList::reverse_iterator;
|
||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||
|
||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
||||
~Block();
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
|
||||
Block(Block&&) = default;
|
||||
Block& operator=(Block&&) = default;
|
||||
|
||||
/// Appends a new instruction to the end of this basic block.
|
||||
void AppendNewInst(Opcode op, std::initializer_list<Value> args);
|
||||
|
||||
/// Prepends a copy of an instruction to this basic block before the insertion point.
|
||||
iterator PrependNewInst(iterator insertion_point, const Inst& base_inst);
|
||||
|
||||
/// Prepends a new instruction to this basic block before the insertion point.
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args = {}, u32 flags = 0);
|
||||
|
||||
/// Adds a new branch to this basic block.
|
||||
void AddBranch(Block* block);
|
||||
|
||||
/// Gets a mutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] InstructionList& Instructions() noexcept {
|
||||
return instructions;
|
||||
}
|
||||
/// Gets an immutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] const InstructionList& Instructions() const noexcept {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/// Gets an immutable span to the immediate predecessors.
|
||||
[[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept {
|
||||
return imm_predecessors;
|
||||
}
|
||||
/// Gets an immutable span to the immediate successors.
|
||||
[[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept {
|
||||
return imm_successors;
|
||||
}
|
||||
|
||||
/// Intrusively store the host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
void SetDefinition(DefinitionType def) {
|
||||
definition = Common::BitCast<u32>(def);
|
||||
}
|
||||
|
||||
/// Return the intrusively stored host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
[[nodiscard]] DefinitionType Definition() const noexcept {
|
||||
return Common::BitCast<DefinitionType>(definition);
|
||||
}
|
||||
|
||||
void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept {
|
||||
ssa_reg_values[RegIndex(reg)] = value;
|
||||
}
|
||||
const Value& SsaRegValue(IR::Reg reg) const noexcept {
|
||||
return ssa_reg_values[RegIndex(reg)];
|
||||
}
|
||||
|
||||
void SsaSeal() noexcept {
|
||||
is_ssa_sealed = true;
|
||||
}
|
||||
[[nodiscard]] bool IsSsaSealed() const noexcept {
|
||||
return is_ssa_sealed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return instructions.empty();
|
||||
}
|
||||
[[nodiscard]] size_type size() const {
|
||||
return instructions.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& front() {
|
||||
return instructions.front();
|
||||
}
|
||||
[[nodiscard]] const Inst& front() const {
|
||||
return instructions.front();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& back() {
|
||||
return instructions.back();
|
||||
}
|
||||
[[nodiscard]] const Inst& back() const {
|
||||
return instructions.back();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] const_iterator begin() const {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] iterator end() {
|
||||
return instructions.end();
|
||||
}
|
||||
[[nodiscard]] const_iterator end() const {
|
||||
return instructions.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rbegin() {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] reverse_iterator rend() {
|
||||
return instructions.rend();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rend() const {
|
||||
return instructions.rend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cbegin() const {
|
||||
return instructions.cbegin();
|
||||
}
|
||||
[[nodiscard]] const_iterator cend() const {
|
||||
return instructions.cend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const {
|
||||
return instructions.crbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator crend() const {
|
||||
return instructions.crend();
|
||||
}
|
||||
|
||||
// Set the order of the block, it can be set pre order, the user decides
|
||||
void SetOrder(u32 new_order) {
|
||||
order = new_order;
|
||||
}
|
||||
|
||||
// Get the order of the block.
|
||||
// The higher, the closer is the block to the end.
|
||||
[[nodiscard]] u32 GetOrder() const {
|
||||
return order;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Memory pool for instruction list
|
||||
ObjectPool<Inst>* inst_pool;
|
||||
|
||||
/// List of instructions in this block
|
||||
InstructionList instructions;
|
||||
|
||||
/// Block immediate predecessors
|
||||
std::vector<Block*> imm_predecessors;
|
||||
/// Block immediate successors
|
||||
std::vector<Block*> imm_successors;
|
||||
|
||||
/// Intrusively store the value of a register in the block.
|
||||
std::array<Value, NUM_REGS> ssa_reg_values;
|
||||
/// Intrusively store if the block is sealed in the SSA pass.
|
||||
bool is_ssa_sealed{false};
|
||||
|
||||
/// Intrusively stored host definition of this block.
|
||||
u32 definition{};
|
||||
|
||||
/// Order of the block.
|
||||
u32 order{};
|
||||
};
|
||||
|
||||
using BlockList = std::vector<Block*>;
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block);
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block,
|
||||
const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index,
|
||||
size_t& inst_index);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
}
|
||||
// Breadth-first search visiting the right most arguments first
|
||||
// Small vector has been determined from shaders in Super Smash Bros. Ultimate
|
||||
boost::container::small_vector<const Inst*, 2> visited;
|
||||
std::queue<const Inst*> queue;
|
||||
queue.push(value.InstRecursive());
|
||||
|
||||
while (!queue.empty()) {
|
||||
// Pop one instruction from the queue
|
||||
const Inst* const inst{queue.front()};
|
||||
queue.pop();
|
||||
if (const std::optional result = pred(inst)) {
|
||||
// This is the instruction we were looking for
|
||||
return result;
|
||||
}
|
||||
// Visit the right most arguments first
|
||||
for (size_t arg = inst->NumArgs(); arg--;) {
|
||||
const Value arg_value{inst->Arg(arg)};
|
||||
if (arg_value.IsImmediate()) {
|
||||
continue;
|
||||
}
|
||||
// Queue instruction if it hasn't been visited
|
||||
const Inst* const arg_inst{arg_value.InstRecursive()};
|
||||
if (std::ranges::find(visited, arg_inst) == visited.end()) {
|
||||
visited.push_back(arg_inst);
|
||||
queue.push(arg_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
// SSA tree has been traversed and the result hasn't been found
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
}
|
||||
// Breadth-first search visiting the right most arguments first
|
||||
// Small vector has been determined from shaders in Super Smash Bros. Ultimate
|
||||
boost::container::small_vector<const Inst*, 2> visited;
|
||||
std::queue<const Inst*> queue;
|
||||
queue.push(value.InstRecursive());
|
||||
|
||||
while (!queue.empty()) {
|
||||
// Pop one instruction from the queue
|
||||
const Inst* const inst{queue.front()};
|
||||
queue.pop();
|
||||
if (const std::optional result = pred(inst)) {
|
||||
// This is the instruction we were looking for
|
||||
return result;
|
||||
}
|
||||
// Visit the right most arguments first
|
||||
for (size_t arg = inst->NumArgs(); arg--;) {
|
||||
const Value arg_value{inst->Arg(arg)};
|
||||
if (arg_value.IsImmediate()) {
|
||||
continue;
|
||||
}
|
||||
// Queue instruction if it hasn't been visited
|
||||
const Inst* const arg_inst{arg_value.InstRecursive()};
|
||||
if (std::ranges::find(visited, arg_inst) == visited.end()) {
|
||||
visited.push_back(arg_inst);
|
||||
queue.push(arg_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
// SSA tree has been traversed and the result hasn't been found
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(Condition condition) {
|
||||
std::string ret;
|
||||
if (condition.GetFlowTest() != FlowTest::T) {
|
||||
ret = fmt::to_string(condition.GetFlowTest());
|
||||
}
|
||||
const auto [pred, negated]{condition.GetPred()};
|
||||
if (!ret.empty()) {
|
||||
ret += '&';
|
||||
}
|
||||
if (negated) {
|
||||
ret += '!';
|
||||
}
|
||||
ret += fmt::to_string(pred);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(Condition condition) {
|
||||
std::string ret;
|
||||
if (condition.GetFlowTest() != FlowTest::T) {
|
||||
ret = fmt::to_string(condition.GetFlowTest());
|
||||
}
|
||||
const auto [pred, negated]{condition.GetPred()};
|
||||
if (!ret.empty()) {
|
||||
ret += '&';
|
||||
}
|
||||
if (negated) {
|
||||
ret += '!';
|
||||
}
|
||||
ret += fmt::to_string(pred);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
#include "shader_recompiler/frontend/ir/pred.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Condition {
|
||||
public:
|
||||
Condition() noexcept = default;
|
||||
|
||||
explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)},
|
||||
pred_negated{pred_negated_ ? u8{1} : u8{0}} {}
|
||||
|
||||
explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: Condition(FlowTest::T, pred_, pred_negated_) {}
|
||||
|
||||
explicit Condition(bool value) : Condition(Pred::PT, !value) {}
|
||||
|
||||
auto operator<=>(const Condition&) const noexcept = default;
|
||||
|
||||
[[nodiscard]] IR::FlowTest GetFlowTest() const noexcept {
|
||||
return static_cast<IR::FlowTest>(flow_test);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<IR::Pred, bool> GetPred() const noexcept {
|
||||
return {static_cast<IR::Pred>(pred), pred_negated != 0};
|
||||
}
|
||||
|
||||
private:
|
||||
u16 flow_test;
|
||||
u8 pred;
|
||||
u8 pred_negated;
|
||||
};
|
||||
|
||||
std::string NameOf(Condition condition);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Condition> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Condition& cond, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond));
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
#include "shader_recompiler/frontend/ir/pred.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Condition {
|
||||
public:
|
||||
Condition() noexcept = default;
|
||||
|
||||
explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)},
|
||||
pred_negated{pred_negated_ ? u8{1} : u8{0}} {}
|
||||
|
||||
explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: Condition(FlowTest::T, pred_, pred_negated_) {}
|
||||
|
||||
explicit Condition(bool value) : Condition(Pred::PT, !value) {}
|
||||
|
||||
auto operator<=>(const Condition&) const noexcept = default;
|
||||
|
||||
[[nodiscard]] IR::FlowTest GetFlowTest() const noexcept {
|
||||
return static_cast<IR::FlowTest>(flow_test);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<IR::Pred, bool> GetPred() const noexcept {
|
||||
return {static_cast<IR::Pred>(pred), pred_negated != 0};
|
||||
}
|
||||
|
||||
private:
|
||||
u16 flow_test;
|
||||
u8 pred;
|
||||
u8 pred_negated;
|
||||
};
|
||||
|
||||
std::string NameOf(Condition condition);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Condition> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Condition& cond, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(FlowTest flow_test) {
|
||||
switch (flow_test) {
|
||||
case FlowTest::F:
|
||||
return "F";
|
||||
case FlowTest::LT:
|
||||
return "LT";
|
||||
case FlowTest::EQ:
|
||||
return "EQ";
|
||||
case FlowTest::LE:
|
||||
return "LE";
|
||||
case FlowTest::GT:
|
||||
return "GT";
|
||||
case FlowTest::NE:
|
||||
return "NE";
|
||||
case FlowTest::GE:
|
||||
return "GE";
|
||||
case FlowTest::NUM:
|
||||
return "NUM";
|
||||
case FlowTest::NaN:
|
||||
return "NAN";
|
||||
case FlowTest::LTU:
|
||||
return "LTU";
|
||||
case FlowTest::EQU:
|
||||
return "EQU";
|
||||
case FlowTest::LEU:
|
||||
return "LEU";
|
||||
case FlowTest::GTU:
|
||||
return "GTU";
|
||||
case FlowTest::NEU:
|
||||
return "NEU";
|
||||
case FlowTest::GEU:
|
||||
return "GEU";
|
||||
case FlowTest::T:
|
||||
return "T";
|
||||
case FlowTest::OFF:
|
||||
return "OFF";
|
||||
case FlowTest::LO:
|
||||
return "LO";
|
||||
case FlowTest::SFF:
|
||||
return "SFF";
|
||||
case FlowTest::LS:
|
||||
return "LS";
|
||||
case FlowTest::HI:
|
||||
return "HI";
|
||||
case FlowTest::SFT:
|
||||
return "SFT";
|
||||
case FlowTest::HS:
|
||||
return "HS";
|
||||
case FlowTest::OFT:
|
||||
return "OFT";
|
||||
case FlowTest::CSM_TA:
|
||||
return "CSM_TA";
|
||||
case FlowTest::CSM_TR:
|
||||
return "CSM_TR";
|
||||
case FlowTest::CSM_MX:
|
||||
return "CSM_MX";
|
||||
case FlowTest::FCSM_TA:
|
||||
return "FCSM_TA";
|
||||
case FlowTest::FCSM_TR:
|
||||
return "FCSM_TR";
|
||||
case FlowTest::FCSM_MX:
|
||||
return "FCSM_MX";
|
||||
case FlowTest::RLE:
|
||||
return "RLE";
|
||||
case FlowTest::RGT:
|
||||
return "RGT";
|
||||
}
|
||||
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(FlowTest flow_test) {
|
||||
switch (flow_test) {
|
||||
case FlowTest::F:
|
||||
return "F";
|
||||
case FlowTest::LT:
|
||||
return "LT";
|
||||
case FlowTest::EQ:
|
||||
return "EQ";
|
||||
case FlowTest::LE:
|
||||
return "LE";
|
||||
case FlowTest::GT:
|
||||
return "GT";
|
||||
case FlowTest::NE:
|
||||
return "NE";
|
||||
case FlowTest::GE:
|
||||
return "GE";
|
||||
case FlowTest::NUM:
|
||||
return "NUM";
|
||||
case FlowTest::NaN:
|
||||
return "NAN";
|
||||
case FlowTest::LTU:
|
||||
return "LTU";
|
||||
case FlowTest::EQU:
|
||||
return "EQU";
|
||||
case FlowTest::LEU:
|
||||
return "LEU";
|
||||
case FlowTest::GTU:
|
||||
return "GTU";
|
||||
case FlowTest::NEU:
|
||||
return "NEU";
|
||||
case FlowTest::GEU:
|
||||
return "GEU";
|
||||
case FlowTest::T:
|
||||
return "T";
|
||||
case FlowTest::OFF:
|
||||
return "OFF";
|
||||
case FlowTest::LO:
|
||||
return "LO";
|
||||
case FlowTest::SFF:
|
||||
return "SFF";
|
||||
case FlowTest::LS:
|
||||
return "LS";
|
||||
case FlowTest::HI:
|
||||
return "HI";
|
||||
case FlowTest::SFT:
|
||||
return "SFT";
|
||||
case FlowTest::HS:
|
||||
return "HS";
|
||||
case FlowTest::OFT:
|
||||
return "OFT";
|
||||
case FlowTest::CSM_TA:
|
||||
return "CSM_TA";
|
||||
case FlowTest::CSM_TR:
|
||||
return "CSM_TR";
|
||||
case FlowTest::CSM_MX:
|
||||
return "CSM_MX";
|
||||
case FlowTest::FCSM_TA:
|
||||
return "FCSM_TA";
|
||||
case FlowTest::FCSM_TR:
|
||||
return "FCSM_TR";
|
||||
case FlowTest::FCSM_MX:
|
||||
return "FCSM_MX";
|
||||
case FlowTest::RLE:
|
||||
return "RLE";
|
||||
case FlowTest::RGT:
|
||||
return "RGT";
|
||||
}
|
||||
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FlowTest : u64 {
|
||||
F,
|
||||
LT,
|
||||
EQ,
|
||||
LE,
|
||||
GT,
|
||||
NE,
|
||||
GE,
|
||||
NUM,
|
||||
NaN,
|
||||
LTU,
|
||||
EQU,
|
||||
LEU,
|
||||
GTU,
|
||||
NEU,
|
||||
GEU,
|
||||
T,
|
||||
OFF,
|
||||
LO,
|
||||
SFF,
|
||||
LS,
|
||||
HI,
|
||||
SFT,
|
||||
HS,
|
||||
OFT,
|
||||
CSM_TA,
|
||||
CSM_TR,
|
||||
CSM_MX,
|
||||
FCSM_TA,
|
||||
FCSM_TR,
|
||||
FCSM_MX,
|
||||
RLE,
|
||||
RGT,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string NameOf(FlowTest flow_test);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::FlowTest> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test));
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FlowTest : u64 {
|
||||
F,
|
||||
LT,
|
||||
EQ,
|
||||
LE,
|
||||
GT,
|
||||
NE,
|
||||
GE,
|
||||
NUM,
|
||||
NaN,
|
||||
LTU,
|
||||
EQU,
|
||||
LEU,
|
||||
GTU,
|
||||
NEU,
|
||||
GEU,
|
||||
T,
|
||||
OFF,
|
||||
LO,
|
||||
SFF,
|
||||
LS,
|
||||
HI,
|
||||
SFT,
|
||||
HS,
|
||||
OFT,
|
||||
CSM_TA,
|
||||
CSM_TR,
|
||||
CSM_MX,
|
||||
FCSM_TA,
|
||||
FCSM_TR,
|
||||
FCSM_MX,
|
||||
RLE,
|
||||
RGT,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string NameOf(FlowTest flow_test);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::FlowTest> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test));
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,421 +1,421 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class IREmitter {
|
||||
public:
|
||||
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
|
||||
explicit IREmitter(Block& block_, Block::iterator insertion_point_)
|
||||
: block{&block_}, insertion_point{insertion_point_} {}
|
||||
|
||||
Block* block;
|
||||
|
||||
[[nodiscard]] U1 Imm1(bool value) const;
|
||||
[[nodiscard]] U8 Imm8(u8 value) const;
|
||||
[[nodiscard]] U16 Imm16(u16 value) const;
|
||||
[[nodiscard]] U32 Imm32(u32 value) const;
|
||||
[[nodiscard]] U32 Imm32(s32 value) const;
|
||||
[[nodiscard]] F32 Imm32(f32 value) const;
|
||||
[[nodiscard]] U64 Imm64(u64 value) const;
|
||||
[[nodiscard]] U64 Imm64(s64 value) const;
|
||||
[[nodiscard]] F64 Imm64(f64 value) const;
|
||||
|
||||
U1 ConditionRef(const U1& value);
|
||||
void Reference(const Value& value);
|
||||
|
||||
void PhiMove(IR::Inst& phi, const Value& value);
|
||||
|
||||
void Prologue();
|
||||
void Epilogue();
|
||||
void DemoteToHelperInvocation();
|
||||
void EmitVertex(const U32& stream);
|
||||
void EndPrimitive(const U32& stream);
|
||||
|
||||
[[nodiscard]] U32 GetReg(IR::Reg reg);
|
||||
void SetReg(IR::Reg reg, const U32& value);
|
||||
|
||||
[[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false);
|
||||
void SetPred(IR::Pred pred, const U1& value);
|
||||
|
||||
[[nodiscard]] U1 GetGotoVariable(u32 id);
|
||||
void SetGotoVariable(u32 id, const U1& value);
|
||||
|
||||
[[nodiscard]] U32 GetIndirectBranchVariable();
|
||||
void SetIndirectBranchVariable(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
|
||||
[[nodiscard]] Value GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize,
|
||||
bool is_signed);
|
||||
[[nodiscard]] F32 GetFloatCbuf(const U32& binding, const U32& byte_offset);
|
||||
|
||||
[[nodiscard]] U1 GetZFlag();
|
||||
[[nodiscard]] U1 GetSFlag();
|
||||
[[nodiscard]] U1 GetCFlag();
|
||||
[[nodiscard]] U1 GetOFlag();
|
||||
|
||||
void SetZFlag(const U1& value);
|
||||
void SetSFlag(const U1& value);
|
||||
void SetCFlag(const U1& value);
|
||||
void SetOFlag(const U1& value);
|
||||
|
||||
[[nodiscard]] U1 Condition(IR::Condition cond);
|
||||
[[nodiscard]] U1 GetFlowTestResult(FlowTest test);
|
||||
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
|
||||
void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex);
|
||||
void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetPatch(Patch patch);
|
||||
void SetPatch(Patch patch, const F32& value);
|
||||
|
||||
void SetFragColor(u32 index, u32 component, const F32& value);
|
||||
void SetSampleMask(const U32& value);
|
||||
void SetFragDepth(const F32& value);
|
||||
|
||||
[[nodiscard]] U32 WorkgroupIdX();
|
||||
[[nodiscard]] U32 WorkgroupIdY();
|
||||
[[nodiscard]] U32 WorkgroupIdZ();
|
||||
|
||||
[[nodiscard]] Value LocalInvocationId();
|
||||
[[nodiscard]] U32 LocalInvocationIdX();
|
||||
[[nodiscard]] U32 LocalInvocationIdY();
|
||||
[[nodiscard]] U32 LocalInvocationIdZ();
|
||||
|
||||
[[nodiscard]] U32 InvocationId();
|
||||
[[nodiscard]] U32 SampleId();
|
||||
[[nodiscard]] U1 IsHelperInvocation();
|
||||
[[nodiscard]] F32 YDirection();
|
||||
|
||||
[[nodiscard]] F32 ResolutionDownFactor();
|
||||
|
||||
[[nodiscard]] U32 LaneId();
|
||||
|
||||
[[nodiscard]] U32 LoadGlobalU8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalU16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobal32(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal64(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal128(const U64& address);
|
||||
|
||||
void WriteGlobalU8(const U64& address, const U32& value);
|
||||
void WriteGlobalS8(const U64& address, const U32& value);
|
||||
void WriteGlobalU16(const U64& address, const U32& value);
|
||||
void WriteGlobalS16(const U64& address, const U32& value);
|
||||
void WriteGlobal32(const U64& address, const U32& value);
|
||||
void WriteGlobal64(const U64& address, const IR::Value& vector);
|
||||
void WriteGlobal128(const U64& address, const IR::Value& vector);
|
||||
|
||||
[[nodiscard]] U32 LoadLocal(const U32& word_offset);
|
||||
void WriteLocal(const U32& word_offset, const U32& value);
|
||||
|
||||
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset);
|
||||
void WriteShared(int bit_size, const U32& offset, const Value& value);
|
||||
|
||||
[[nodiscard]] U1 GetZeroFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSignFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetCarryFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetOverflowFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSparseFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetInBoundsFromOp(const Value& op);
|
||||
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3,
|
||||
const Value& e4);
|
||||
[[nodiscard]] Value CompositeExtract(const Value& vector, size_t element);
|
||||
[[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element);
|
||||
|
||||
[[nodiscard]] Value Select(const U1& condition, const Value& true_value,
|
||||
const Value& false_value);
|
||||
|
||||
void Barrier();
|
||||
void WorkgroupMemoryBarrier();
|
||||
void DeviceMemoryBarrier();
|
||||
|
||||
template <typename Dest, typename Source>
|
||||
[[nodiscard]] Dest BitCast(const Source& value);
|
||||
|
||||
[[nodiscard]] U64 PackUint2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackUint2x32(const U64& value);
|
||||
|
||||
[[nodiscard]] U32 PackFloat2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackFloat2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 PackHalf2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackHalf2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] F64 PackDouble2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackDouble2x32(const F64& value);
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAdd(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPMul(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFma(const F16F32F64& a, const F16F32F64& b, const F16F32F64& c,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAbs(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPNeg(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPAbsNeg(const F16F32F64& value, bool abs, bool neg);
|
||||
|
||||
[[nodiscard]] F32 FPCos(const F32& value);
|
||||
[[nodiscard]] F32 FPSin(const F32& value);
|
||||
[[nodiscard]] F32 FPExp2(const F32& value);
|
||||
[[nodiscard]] F32 FPLog2(const F32& value);
|
||||
[[nodiscard]] F32F64 FPRecip(const F32F64& value);
|
||||
[[nodiscard]] F32F64 FPRecipSqrt(const F32F64& value);
|
||||
[[nodiscard]] F32 FPSqrt(const F32& value);
|
||||
[[nodiscard]] F16F32F64 FPSaturate(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPClamp(const F16F32F64& value, const F16F32F64& min_value,
|
||||
const F16F32F64& max_value);
|
||||
[[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 FPEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPNotEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThan(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPIsNan(const F16F32F64& value);
|
||||
[[nodiscard]] U1 FPOrdered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] U1 FPUnordered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
[[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32 IMul(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IDiv(const U32& a, const U32& b, bool is_signed = false);
|
||||
[[nodiscard]] U32U64 INeg(const U32U64& value);
|
||||
[[nodiscard]] U32 IAbs(const U32& value);
|
||||
[[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseOr(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset,
|
||||
const U32& count);
|
||||
[[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count,
|
||||
bool is_signed = false);
|
||||
[[nodiscard]] U32 BitReverse(const U32& value);
|
||||
[[nodiscard]] U32 BitCount(const U32& value);
|
||||
[[nodiscard]] U32 BitwiseNot(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 FindSMsb(const U32& value);
|
||||
[[nodiscard]] U32 FindUMsb(const U32& value);
|
||||
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
|
||||
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
|
||||
|
||||
[[nodiscard]] U1 ILessThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
|
||||
[[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs);
|
||||
[[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
|
||||
[[nodiscard]] U32 SharedAtomicIAdd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicSMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMin(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicSMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMax(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicInc(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicDec(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicAnd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicOr(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicXor(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 SharedAtomicExchange(const U32& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] U32U64 GlobalAtomicIAdd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMin(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMax(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32 GlobalAtomicInc(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 GlobalAtomicDec(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicAnd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicOr(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicXor(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicExchange(const U64& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] F32 GlobalAtomicF32Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Min(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Max(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalXor(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalNot(const U1& value);
|
||||
|
||||
[[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed,
|
||||
const Value& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 UConvert(size_t result_bitsize, const U32U64& value);
|
||||
[[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& bias, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& lod, const Value& offset,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& bias,
|
||||
const Value& offset, const F32& lod_clamp,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& lod,
|
||||
const Value& offset, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
|
||||
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
|
||||
const Value& offset2, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
|
||||
const Value& offset, const Value& offset2, const F32& dref,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
|
||||
const U32& lod, const U32& multisampling, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
|
||||
const Value& derivates, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
|
||||
void ImageWrite(const Value& handle, const Value& coords, const Value& color,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMin(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicDec(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicOr(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicXor(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] U1 IsTextureScaled(const U32& index);
|
||||
[[nodiscard]] U1 IsImageScaled(const U32& index);
|
||||
|
||||
[[nodiscard]] U1 VoteAll(const U1& value);
|
||||
[[nodiscard]] U1 VoteAny(const U1& value);
|
||||
[[nodiscard]] U1 VoteEqual(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupBallot(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupEqMask();
|
||||
[[nodiscard]] U32 SubgroupLtMask();
|
||||
[[nodiscard]] U32 SubgroupLeMask();
|
||||
[[nodiscard]] U32 SubgroupGtMask();
|
||||
[[nodiscard]] U32 SubgroupGeMask();
|
||||
[[nodiscard]] U32 ShuffleIndex(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleUp(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleDown(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
||||
const IR::U32& clamp, const IR::U32& seg_mask);
|
||||
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F32 DPdxFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdxCoarse(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyCoarse(const F32& a);
|
||||
|
||||
private:
|
||||
IR::Block::iterator insertion_point;
|
||||
|
||||
template <typename T = Value, typename... Args>
|
||||
T Inst(Opcode op, Args... args) {
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags {
|
||||
Flags() = default;
|
||||
Flags(T proxy_) : proxy{proxy_} {}
|
||||
|
||||
T proxy;
|
||||
};
|
||||
|
||||
template <typename T = Value, typename FlagType, typename... Args>
|
||||
T Inst(Opcode op, Flags<FlagType> flags, Args... args) {
|
||||
u32 raw_flags{};
|
||||
std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy));
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class IREmitter {
|
||||
public:
|
||||
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
|
||||
explicit IREmitter(Block& block_, Block::iterator insertion_point_)
|
||||
: block{&block_}, insertion_point{insertion_point_} {}
|
||||
|
||||
Block* block;
|
||||
|
||||
[[nodiscard]] U1 Imm1(bool value) const;
|
||||
[[nodiscard]] U8 Imm8(u8 value) const;
|
||||
[[nodiscard]] U16 Imm16(u16 value) const;
|
||||
[[nodiscard]] U32 Imm32(u32 value) const;
|
||||
[[nodiscard]] U32 Imm32(s32 value) const;
|
||||
[[nodiscard]] F32 Imm32(f32 value) const;
|
||||
[[nodiscard]] U64 Imm64(u64 value) const;
|
||||
[[nodiscard]] U64 Imm64(s64 value) const;
|
||||
[[nodiscard]] F64 Imm64(f64 value) const;
|
||||
|
||||
U1 ConditionRef(const U1& value);
|
||||
void Reference(const Value& value);
|
||||
|
||||
void PhiMove(IR::Inst& phi, const Value& value);
|
||||
|
||||
void Prologue();
|
||||
void Epilogue();
|
||||
void DemoteToHelperInvocation();
|
||||
void EmitVertex(const U32& stream);
|
||||
void EndPrimitive(const U32& stream);
|
||||
|
||||
[[nodiscard]] U32 GetReg(IR::Reg reg);
|
||||
void SetReg(IR::Reg reg, const U32& value);
|
||||
|
||||
[[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false);
|
||||
void SetPred(IR::Pred pred, const U1& value);
|
||||
|
||||
[[nodiscard]] U1 GetGotoVariable(u32 id);
|
||||
void SetGotoVariable(u32 id, const U1& value);
|
||||
|
||||
[[nodiscard]] U32 GetIndirectBranchVariable();
|
||||
void SetIndirectBranchVariable(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
|
||||
[[nodiscard]] Value GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize,
|
||||
bool is_signed);
|
||||
[[nodiscard]] F32 GetFloatCbuf(const U32& binding, const U32& byte_offset);
|
||||
|
||||
[[nodiscard]] U1 GetZFlag();
|
||||
[[nodiscard]] U1 GetSFlag();
|
||||
[[nodiscard]] U1 GetCFlag();
|
||||
[[nodiscard]] U1 GetOFlag();
|
||||
|
||||
void SetZFlag(const U1& value);
|
||||
void SetSFlag(const U1& value);
|
||||
void SetCFlag(const U1& value);
|
||||
void SetOFlag(const U1& value);
|
||||
|
||||
[[nodiscard]] U1 Condition(IR::Condition cond);
|
||||
[[nodiscard]] U1 GetFlowTestResult(FlowTest test);
|
||||
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
|
||||
void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex);
|
||||
void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetPatch(Patch patch);
|
||||
void SetPatch(Patch patch, const F32& value);
|
||||
|
||||
void SetFragColor(u32 index, u32 component, const F32& value);
|
||||
void SetSampleMask(const U32& value);
|
||||
void SetFragDepth(const F32& value);
|
||||
|
||||
[[nodiscard]] U32 WorkgroupIdX();
|
||||
[[nodiscard]] U32 WorkgroupIdY();
|
||||
[[nodiscard]] U32 WorkgroupIdZ();
|
||||
|
||||
[[nodiscard]] Value LocalInvocationId();
|
||||
[[nodiscard]] U32 LocalInvocationIdX();
|
||||
[[nodiscard]] U32 LocalInvocationIdY();
|
||||
[[nodiscard]] U32 LocalInvocationIdZ();
|
||||
|
||||
[[nodiscard]] U32 InvocationId();
|
||||
[[nodiscard]] U32 SampleId();
|
||||
[[nodiscard]] U1 IsHelperInvocation();
|
||||
[[nodiscard]] F32 YDirection();
|
||||
|
||||
[[nodiscard]] F32 ResolutionDownFactor();
|
||||
|
||||
[[nodiscard]] U32 LaneId();
|
||||
|
||||
[[nodiscard]] U32 LoadGlobalU8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalU16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobal32(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal64(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal128(const U64& address);
|
||||
|
||||
void WriteGlobalU8(const U64& address, const U32& value);
|
||||
void WriteGlobalS8(const U64& address, const U32& value);
|
||||
void WriteGlobalU16(const U64& address, const U32& value);
|
||||
void WriteGlobalS16(const U64& address, const U32& value);
|
||||
void WriteGlobal32(const U64& address, const U32& value);
|
||||
void WriteGlobal64(const U64& address, const IR::Value& vector);
|
||||
void WriteGlobal128(const U64& address, const IR::Value& vector);
|
||||
|
||||
[[nodiscard]] U32 LoadLocal(const U32& word_offset);
|
||||
void WriteLocal(const U32& word_offset, const U32& value);
|
||||
|
||||
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset);
|
||||
void WriteShared(int bit_size, const U32& offset, const Value& value);
|
||||
|
||||
[[nodiscard]] U1 GetZeroFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSignFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetCarryFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetOverflowFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSparseFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetInBoundsFromOp(const Value& op);
|
||||
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3,
|
||||
const Value& e4);
|
||||
[[nodiscard]] Value CompositeExtract(const Value& vector, size_t element);
|
||||
[[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element);
|
||||
|
||||
[[nodiscard]] Value Select(const U1& condition, const Value& true_value,
|
||||
const Value& false_value);
|
||||
|
||||
void Barrier();
|
||||
void WorkgroupMemoryBarrier();
|
||||
void DeviceMemoryBarrier();
|
||||
|
||||
template <typename Dest, typename Source>
|
||||
[[nodiscard]] Dest BitCast(const Source& value);
|
||||
|
||||
[[nodiscard]] U64 PackUint2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackUint2x32(const U64& value);
|
||||
|
||||
[[nodiscard]] U32 PackFloat2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackFloat2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 PackHalf2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackHalf2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] F64 PackDouble2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackDouble2x32(const F64& value);
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAdd(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPMul(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFma(const F16F32F64& a, const F16F32F64& b, const F16F32F64& c,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAbs(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPNeg(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPAbsNeg(const F16F32F64& value, bool abs, bool neg);
|
||||
|
||||
[[nodiscard]] F32 FPCos(const F32& value);
|
||||
[[nodiscard]] F32 FPSin(const F32& value);
|
||||
[[nodiscard]] F32 FPExp2(const F32& value);
|
||||
[[nodiscard]] F32 FPLog2(const F32& value);
|
||||
[[nodiscard]] F32F64 FPRecip(const F32F64& value);
|
||||
[[nodiscard]] F32F64 FPRecipSqrt(const F32F64& value);
|
||||
[[nodiscard]] F32 FPSqrt(const F32& value);
|
||||
[[nodiscard]] F16F32F64 FPSaturate(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPClamp(const F16F32F64& value, const F16F32F64& min_value,
|
||||
const F16F32F64& max_value);
|
||||
[[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 FPEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPNotEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThan(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPIsNan(const F16F32F64& value);
|
||||
[[nodiscard]] U1 FPOrdered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] U1 FPUnordered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
[[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32 IMul(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IDiv(const U32& a, const U32& b, bool is_signed = false);
|
||||
[[nodiscard]] U32U64 INeg(const U32U64& value);
|
||||
[[nodiscard]] U32 IAbs(const U32& value);
|
||||
[[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseOr(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset,
|
||||
const U32& count);
|
||||
[[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count,
|
||||
bool is_signed = false);
|
||||
[[nodiscard]] U32 BitReverse(const U32& value);
|
||||
[[nodiscard]] U32 BitCount(const U32& value);
|
||||
[[nodiscard]] U32 BitwiseNot(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 FindSMsb(const U32& value);
|
||||
[[nodiscard]] U32 FindUMsb(const U32& value);
|
||||
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
|
||||
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
|
||||
|
||||
[[nodiscard]] U1 ILessThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
|
||||
[[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs);
|
||||
[[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
|
||||
[[nodiscard]] U32 SharedAtomicIAdd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicSMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMin(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicSMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMax(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicInc(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicDec(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicAnd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicOr(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicXor(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 SharedAtomicExchange(const U32& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] U32U64 GlobalAtomicIAdd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMin(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMax(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32 GlobalAtomicInc(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 GlobalAtomicDec(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicAnd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicOr(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicXor(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicExchange(const U64& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] F32 GlobalAtomicF32Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Min(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Max(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalXor(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalNot(const U1& value);
|
||||
|
||||
[[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed,
|
||||
const Value& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 UConvert(size_t result_bitsize, const U32U64& value);
|
||||
[[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& bias, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& lod, const Value& offset,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& bias,
|
||||
const Value& offset, const F32& lod_clamp,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& lod,
|
||||
const Value& offset, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
|
||||
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
|
||||
const Value& offset2, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
|
||||
const Value& offset, const Value& offset2, const F32& dref,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
|
||||
const U32& lod, const U32& multisampling, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
|
||||
const Value& derivates, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
|
||||
void ImageWrite(const Value& handle, const Value& coords, const Value& color,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMin(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicDec(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicOr(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicXor(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] U1 IsTextureScaled(const U32& index);
|
||||
[[nodiscard]] U1 IsImageScaled(const U32& index);
|
||||
|
||||
[[nodiscard]] U1 VoteAll(const U1& value);
|
||||
[[nodiscard]] U1 VoteAny(const U1& value);
|
||||
[[nodiscard]] U1 VoteEqual(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupBallot(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupEqMask();
|
||||
[[nodiscard]] U32 SubgroupLtMask();
|
||||
[[nodiscard]] U32 SubgroupLeMask();
|
||||
[[nodiscard]] U32 SubgroupGtMask();
|
||||
[[nodiscard]] U32 SubgroupGeMask();
|
||||
[[nodiscard]] U32 ShuffleIndex(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleUp(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleDown(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
||||
const IR::U32& clamp, const IR::U32& seg_mask);
|
||||
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F32 DPdxFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdxCoarse(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyCoarse(const F32& a);
|
||||
|
||||
private:
|
||||
IR::Block::iterator insertion_point;
|
||||
|
||||
template <typename T = Value, typename... Args>
|
||||
T Inst(Opcode op, Args... args) {
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags {
|
||||
Flags() = default;
|
||||
Flags(T proxy_) : proxy{proxy_} {}
|
||||
|
||||
T proxy;
|
||||
};
|
||||
|
||||
template <typename T = Value, typename FlagType, typename... Args>
|
||||
T Inst(Opcode op, Flags<FlagType> flags, Args... args) {
|
||||
u32 raw_flags{};
|
||||
std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy));
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,455 +1,455 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
namespace {
|
||||
void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
|
||||
if (inst && inst->GetOpcode() != opcode) {
|
||||
throw LogicError("Invalid pseudo-instruction");
|
||||
}
|
||||
}
|
||||
|
||||
void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
|
||||
if (dest_inst) {
|
||||
throw LogicError("Only one of each type of pseudo-op allowed");
|
||||
}
|
||||
dest_inst = pseudo_inst;
|
||||
}
|
||||
|
||||
void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
|
||||
if (inst->GetOpcode() != expected_opcode) {
|
||||
throw LogicError("Undoing use of invalid pseudo-op");
|
||||
}
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
void AllocAssociatedInsts(std::unique_ptr<AssociatedInsts>& associated_insts) {
|
||||
if (!associated_insts) {
|
||||
associated_insts = std::make_unique<AssociatedInsts>();
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} {
|
||||
if (op == Opcode::Phi) {
|
||||
std::construct_at(&phi_args);
|
||||
} else {
|
||||
std::construct_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Inst(const Inst& base) : op{base.op}, flags{base.flags} {
|
||||
if (base.op == Opcode::Phi) {
|
||||
throw NotImplementedException("Copying phi node");
|
||||
}
|
||||
std::construct_at(&args);
|
||||
const size_t num_args{base.NumArgs()};
|
||||
for (size_t index = 0; index < num_args; ++index) {
|
||||
SetArg(index, base.Arg(index));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::~Inst() {
|
||||
if (op == Opcode::Phi) {
|
||||
std::destroy_at(&phi_args);
|
||||
} else {
|
||||
std::destroy_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::MayHaveSideEffects() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::ConditionRef:
|
||||
case Opcode::Reference:
|
||||
case Opcode::PhiMove:
|
||||
case Opcode::Prologue:
|
||||
case Opcode::Epilogue:
|
||||
case Opcode::Join:
|
||||
case Opcode::DemoteToHelperInvocation:
|
||||
case Opcode::Barrier:
|
||||
case Opcode::WorkgroupMemoryBarrier:
|
||||
case Opcode::DeviceMemoryBarrier:
|
||||
case Opcode::EmitVertex:
|
||||
case Opcode::EndPrimitive:
|
||||
case Opcode::SetAttribute:
|
||||
case Opcode::SetAttributeIndexed:
|
||||
case Opcode::SetPatch:
|
||||
case Opcode::SetFragColor:
|
||||
case Opcode::SetSampleMask:
|
||||
case Opcode::SetFragDepth:
|
||||
case Opcode::WriteGlobalU8:
|
||||
case Opcode::WriteGlobalS8:
|
||||
case Opcode::WriteGlobalU16:
|
||||
case Opcode::WriteGlobalS16:
|
||||
case Opcode::WriteGlobal32:
|
||||
case Opcode::WriteGlobal64:
|
||||
case Opcode::WriteGlobal128:
|
||||
case Opcode::WriteStorageU8:
|
||||
case Opcode::WriteStorageS8:
|
||||
case Opcode::WriteStorageU16:
|
||||
case Opcode::WriteStorageS16:
|
||||
case Opcode::WriteStorage32:
|
||||
case Opcode::WriteStorage64:
|
||||
case Opcode::WriteStorage128:
|
||||
case Opcode::WriteLocal:
|
||||
case Opcode::WriteSharedU8:
|
||||
case Opcode::WriteSharedU16:
|
||||
case Opcode::WriteSharedU32:
|
||||
case Opcode::WriteSharedU64:
|
||||
case Opcode::WriteSharedU128:
|
||||
case Opcode::SharedAtomicIAdd32:
|
||||
case Opcode::SharedAtomicSMin32:
|
||||
case Opcode::SharedAtomicUMin32:
|
||||
case Opcode::SharedAtomicSMax32:
|
||||
case Opcode::SharedAtomicUMax32:
|
||||
case Opcode::SharedAtomicInc32:
|
||||
case Opcode::SharedAtomicDec32:
|
||||
case Opcode::SharedAtomicAnd32:
|
||||
case Opcode::SharedAtomicOr32:
|
||||
case Opcode::SharedAtomicXor32:
|
||||
case Opcode::SharedAtomicExchange32:
|
||||
case Opcode::SharedAtomicExchange64:
|
||||
case Opcode::SharedAtomicExchange32x2:
|
||||
case Opcode::GlobalAtomicIAdd32:
|
||||
case Opcode::GlobalAtomicSMin32:
|
||||
case Opcode::GlobalAtomicUMin32:
|
||||
case Opcode::GlobalAtomicSMax32:
|
||||
case Opcode::GlobalAtomicUMax32:
|
||||
case Opcode::GlobalAtomicInc32:
|
||||
case Opcode::GlobalAtomicDec32:
|
||||
case Opcode::GlobalAtomicAnd32:
|
||||
case Opcode::GlobalAtomicOr32:
|
||||
case Opcode::GlobalAtomicXor32:
|
||||
case Opcode::GlobalAtomicExchange32:
|
||||
case Opcode::GlobalAtomicIAdd64:
|
||||
case Opcode::GlobalAtomicSMin64:
|
||||
case Opcode::GlobalAtomicUMin64:
|
||||
case Opcode::GlobalAtomicSMax64:
|
||||
case Opcode::GlobalAtomicUMax64:
|
||||
case Opcode::GlobalAtomicAnd64:
|
||||
case Opcode::GlobalAtomicOr64:
|
||||
case Opcode::GlobalAtomicXor64:
|
||||
case Opcode::GlobalAtomicExchange64:
|
||||
case Opcode::GlobalAtomicIAdd32x2:
|
||||
case Opcode::GlobalAtomicSMin32x2:
|
||||
case Opcode::GlobalAtomicUMin32x2:
|
||||
case Opcode::GlobalAtomicSMax32x2:
|
||||
case Opcode::GlobalAtomicUMax32x2:
|
||||
case Opcode::GlobalAtomicAnd32x2:
|
||||
case Opcode::GlobalAtomicOr32x2:
|
||||
case Opcode::GlobalAtomicXor32x2:
|
||||
case Opcode::GlobalAtomicExchange32x2:
|
||||
case Opcode::GlobalAtomicAddF32:
|
||||
case Opcode::GlobalAtomicAddF16x2:
|
||||
case Opcode::GlobalAtomicAddF32x2:
|
||||
case Opcode::GlobalAtomicMinF16x2:
|
||||
case Opcode::GlobalAtomicMinF32x2:
|
||||
case Opcode::GlobalAtomicMaxF16x2:
|
||||
case Opcode::GlobalAtomicMaxF32x2:
|
||||
case Opcode::StorageAtomicIAdd32:
|
||||
case Opcode::StorageAtomicSMin32:
|
||||
case Opcode::StorageAtomicUMin32:
|
||||
case Opcode::StorageAtomicSMax32:
|
||||
case Opcode::StorageAtomicUMax32:
|
||||
case Opcode::StorageAtomicInc32:
|
||||
case Opcode::StorageAtomicDec32:
|
||||
case Opcode::StorageAtomicAnd32:
|
||||
case Opcode::StorageAtomicOr32:
|
||||
case Opcode::StorageAtomicXor32:
|
||||
case Opcode::StorageAtomicExchange32:
|
||||
case Opcode::StorageAtomicIAdd64:
|
||||
case Opcode::StorageAtomicSMin64:
|
||||
case Opcode::StorageAtomicUMin64:
|
||||
case Opcode::StorageAtomicSMax64:
|
||||
case Opcode::StorageAtomicUMax64:
|
||||
case Opcode::StorageAtomicAnd64:
|
||||
case Opcode::StorageAtomicOr64:
|
||||
case Opcode::StorageAtomicXor64:
|
||||
case Opcode::StorageAtomicExchange64:
|
||||
case Opcode::StorageAtomicIAdd32x2:
|
||||
case Opcode::StorageAtomicSMin32x2:
|
||||
case Opcode::StorageAtomicUMin32x2:
|
||||
case Opcode::StorageAtomicSMax32x2:
|
||||
case Opcode::StorageAtomicUMax32x2:
|
||||
case Opcode::StorageAtomicAnd32x2:
|
||||
case Opcode::StorageAtomicOr32x2:
|
||||
case Opcode::StorageAtomicXor32x2:
|
||||
case Opcode::StorageAtomicExchange32x2:
|
||||
case Opcode::StorageAtomicAddF32:
|
||||
case Opcode::StorageAtomicAddF16x2:
|
||||
case Opcode::StorageAtomicAddF32x2:
|
||||
case Opcode::StorageAtomicMinF16x2:
|
||||
case Opcode::StorageAtomicMinF32x2:
|
||||
case Opcode::StorageAtomicMaxF16x2:
|
||||
case Opcode::StorageAtomicMaxF32x2:
|
||||
case Opcode::BindlessImageWrite:
|
||||
case Opcode::BoundImageWrite:
|
||||
case Opcode::ImageWrite:
|
||||
case IR::Opcode::BindlessImageAtomicIAdd32:
|
||||
case IR::Opcode::BindlessImageAtomicSMin32:
|
||||
case IR::Opcode::BindlessImageAtomicUMin32:
|
||||
case IR::Opcode::BindlessImageAtomicSMax32:
|
||||
case IR::Opcode::BindlessImageAtomicUMax32:
|
||||
case IR::Opcode::BindlessImageAtomicInc32:
|
||||
case IR::Opcode::BindlessImageAtomicDec32:
|
||||
case IR::Opcode::BindlessImageAtomicAnd32:
|
||||
case IR::Opcode::BindlessImageAtomicOr32:
|
||||
case IR::Opcode::BindlessImageAtomicXor32:
|
||||
case IR::Opcode::BindlessImageAtomicExchange32:
|
||||
case IR::Opcode::BoundImageAtomicIAdd32:
|
||||
case IR::Opcode::BoundImageAtomicSMin32:
|
||||
case IR::Opcode::BoundImageAtomicUMin32:
|
||||
case IR::Opcode::BoundImageAtomicSMax32:
|
||||
case IR::Opcode::BoundImageAtomicUMax32:
|
||||
case IR::Opcode::BoundImageAtomicInc32:
|
||||
case IR::Opcode::BoundImageAtomicDec32:
|
||||
case IR::Opcode::BoundImageAtomicAnd32:
|
||||
case IR::Opcode::BoundImageAtomicOr32:
|
||||
case IR::Opcode::BoundImageAtomicXor32:
|
||||
case IR::Opcode::BoundImageAtomicExchange32:
|
||||
case IR::Opcode::ImageAtomicIAdd32:
|
||||
case IR::Opcode::ImageAtomicSMin32:
|
||||
case IR::Opcode::ImageAtomicUMin32:
|
||||
case IR::Opcode::ImageAtomicSMax32:
|
||||
case IR::Opcode::ImageAtomicUMax32:
|
||||
case IR::Opcode::ImageAtomicInc32:
|
||||
case IR::Opcode::ImageAtomicDec32:
|
||||
case IR::Opcode::ImageAtomicAnd32:
|
||||
case IR::Opcode::ImageAtomicOr32:
|
||||
case IR::Opcode::ImageAtomicXor32:
|
||||
case IR::Opcode::ImageAtomicExchange32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::IsPseudoInstruction() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
case Opcode::GetSignFromOp:
|
||||
case Opcode::GetCarryFromOp:
|
||||
case Opcode::GetOverflowFromOp:
|
||||
case Opcode::GetSparseFromOp:
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::AreAllArgsImmediates() const {
|
||||
if (op == Opcode::Phi) {
|
||||
throw LogicError("Testing for all arguments are immediates on phi instruction");
|
||||
}
|
||||
return std::all_of(args.begin(), args.begin() + NumArgs(),
|
||||
[](const IR::Value& value) { return value.IsImmediate(); });
|
||||
}
|
||||
|
||||
Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
|
||||
if (!associated_insts) {
|
||||
return nullptr;
|
||||
}
|
||||
switch (opcode) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
CheckPseudoInstruction(associated_insts->zero_inst, Opcode::GetZeroFromOp);
|
||||
return associated_insts->zero_inst;
|
||||
case Opcode::GetSignFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sign_inst, Opcode::GetSignFromOp);
|
||||
return associated_insts->sign_inst;
|
||||
case Opcode::GetCarryFromOp:
|
||||
CheckPseudoInstruction(associated_insts->carry_inst, Opcode::GetCarryFromOp);
|
||||
return associated_insts->carry_inst;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
CheckPseudoInstruction(associated_insts->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
return associated_insts->overflow_inst;
|
||||
case Opcode::GetSparseFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sparse_inst, Opcode::GetSparseFromOp);
|
||||
return associated_insts->sparse_inst;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
CheckPseudoInstruction(associated_insts->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
return associated_insts->in_bounds_inst;
|
||||
default:
|
||||
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Type Inst::Type() const {
|
||||
if (op == IR::Opcode::Phi) {
|
||||
// The type of a phi node is stored in its flags
|
||||
return Flags<IR::Type>();
|
||||
}
|
||||
return TypeOf(op);
|
||||
}
|
||||
|
||||
void Inst::SetArg(size_t index, Value value) {
|
||||
if (index >= NumArgs()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
|
||||
}
|
||||
const IR::Value arg{Arg(index)};
|
||||
if (!arg.IsImmediate()) {
|
||||
UndoUse(arg);
|
||||
}
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
phi_args[index].second = value;
|
||||
} else {
|
||||
args[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Block* Inst::PhiBlock(size_t index) const {
|
||||
if (op != Opcode::Phi) {
|
||||
throw LogicError("{} is not a Phi instruction", op);
|
||||
}
|
||||
if (index >= phi_args.size()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in phi instruction");
|
||||
}
|
||||
return phi_args[index].first;
|
||||
}
|
||||
|
||||
void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
phi_args.emplace_back(predecessor, value);
|
||||
}
|
||||
|
||||
void Inst::OrderPhiArgs() {
|
||||
if (op != Opcode::Phi) {
|
||||
throw LogicError("{} is not a Phi instruction", op);
|
||||
}
|
||||
std::sort(phi_args.begin(), phi_args.end(),
|
||||
[](const std::pair<Block*, Value>& a, const std::pair<Block*, Value>& b) {
|
||||
return a.first->GetOrder() < b.first->GetOrder();
|
||||
});
|
||||
}
|
||||
|
||||
void Inst::Invalidate() {
|
||||
ClearArgs();
|
||||
ReplaceOpcode(Opcode::Void);
|
||||
}
|
||||
|
||||
void Inst::ClearArgs() {
|
||||
if (op == Opcode::Phi) {
|
||||
for (auto& pair : phi_args) {
|
||||
IR::Value& value{pair.second};
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
phi_args.clear();
|
||||
} else {
|
||||
for (auto& value : args) {
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
// Reset arguments to null
|
||||
// std::memset was measured to be faster on MSVC than std::ranges:fill
|
||||
std::memset(reinterpret_cast<char*>(&args), 0, sizeof(args));
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::ReplaceUsesWith(Value replacement) {
|
||||
Invalidate();
|
||||
ReplaceOpcode(Opcode::Identity);
|
||||
if (!replacement.IsImmediate()) {
|
||||
Use(replacement);
|
||||
}
|
||||
args[0] = replacement;
|
||||
}
|
||||
|
||||
void Inst::ReplaceOpcode(IR::Opcode opcode) {
|
||||
if (opcode == IR::Opcode::Phi) {
|
||||
throw LogicError("Cannot transition into Phi");
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
// Transition out of phi arguments into non-phi
|
||||
std::destroy_at(&phi_args);
|
||||
std::construct_at(&args);
|
||||
}
|
||||
op = opcode;
|
||||
}
|
||||
|
||||
void Inst::Use(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
++inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->zero_inst, this);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sign_inst, this);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->carry_inst, this);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->overflow_inst, this);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sparse_inst, this);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->in_bounds_inst, this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::UndoUse(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
--inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->zero_inst, Opcode::GetZeroFromOp);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sign_inst, Opcode::GetSignFromOp);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->carry_inst, Opcode::GetCarryFromOp);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sparse_inst, Opcode::GetSparseFromOp);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
namespace {
|
||||
void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
|
||||
if (inst && inst->GetOpcode() != opcode) {
|
||||
throw LogicError("Invalid pseudo-instruction");
|
||||
}
|
||||
}
|
||||
|
||||
void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
|
||||
if (dest_inst) {
|
||||
throw LogicError("Only one of each type of pseudo-op allowed");
|
||||
}
|
||||
dest_inst = pseudo_inst;
|
||||
}
|
||||
|
||||
void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
|
||||
if (inst->GetOpcode() != expected_opcode) {
|
||||
throw LogicError("Undoing use of invalid pseudo-op");
|
||||
}
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
void AllocAssociatedInsts(std::unique_ptr<AssociatedInsts>& associated_insts) {
|
||||
if (!associated_insts) {
|
||||
associated_insts = std::make_unique<AssociatedInsts>();
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} {
|
||||
if (op == Opcode::Phi) {
|
||||
std::construct_at(&phi_args);
|
||||
} else {
|
||||
std::construct_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Inst(const Inst& base) : op{base.op}, flags{base.flags} {
|
||||
if (base.op == Opcode::Phi) {
|
||||
throw NotImplementedException("Copying phi node");
|
||||
}
|
||||
std::construct_at(&args);
|
||||
const size_t num_args{base.NumArgs()};
|
||||
for (size_t index = 0; index < num_args; ++index) {
|
||||
SetArg(index, base.Arg(index));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::~Inst() {
|
||||
if (op == Opcode::Phi) {
|
||||
std::destroy_at(&phi_args);
|
||||
} else {
|
||||
std::destroy_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::MayHaveSideEffects() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::ConditionRef:
|
||||
case Opcode::Reference:
|
||||
case Opcode::PhiMove:
|
||||
case Opcode::Prologue:
|
||||
case Opcode::Epilogue:
|
||||
case Opcode::Join:
|
||||
case Opcode::DemoteToHelperInvocation:
|
||||
case Opcode::Barrier:
|
||||
case Opcode::WorkgroupMemoryBarrier:
|
||||
case Opcode::DeviceMemoryBarrier:
|
||||
case Opcode::EmitVertex:
|
||||
case Opcode::EndPrimitive:
|
||||
case Opcode::SetAttribute:
|
||||
case Opcode::SetAttributeIndexed:
|
||||
case Opcode::SetPatch:
|
||||
case Opcode::SetFragColor:
|
||||
case Opcode::SetSampleMask:
|
||||
case Opcode::SetFragDepth:
|
||||
case Opcode::WriteGlobalU8:
|
||||
case Opcode::WriteGlobalS8:
|
||||
case Opcode::WriteGlobalU16:
|
||||
case Opcode::WriteGlobalS16:
|
||||
case Opcode::WriteGlobal32:
|
||||
case Opcode::WriteGlobal64:
|
||||
case Opcode::WriteGlobal128:
|
||||
case Opcode::WriteStorageU8:
|
||||
case Opcode::WriteStorageS8:
|
||||
case Opcode::WriteStorageU16:
|
||||
case Opcode::WriteStorageS16:
|
||||
case Opcode::WriteStorage32:
|
||||
case Opcode::WriteStorage64:
|
||||
case Opcode::WriteStorage128:
|
||||
case Opcode::WriteLocal:
|
||||
case Opcode::WriteSharedU8:
|
||||
case Opcode::WriteSharedU16:
|
||||
case Opcode::WriteSharedU32:
|
||||
case Opcode::WriteSharedU64:
|
||||
case Opcode::WriteSharedU128:
|
||||
case Opcode::SharedAtomicIAdd32:
|
||||
case Opcode::SharedAtomicSMin32:
|
||||
case Opcode::SharedAtomicUMin32:
|
||||
case Opcode::SharedAtomicSMax32:
|
||||
case Opcode::SharedAtomicUMax32:
|
||||
case Opcode::SharedAtomicInc32:
|
||||
case Opcode::SharedAtomicDec32:
|
||||
case Opcode::SharedAtomicAnd32:
|
||||
case Opcode::SharedAtomicOr32:
|
||||
case Opcode::SharedAtomicXor32:
|
||||
case Opcode::SharedAtomicExchange32:
|
||||
case Opcode::SharedAtomicExchange64:
|
||||
case Opcode::SharedAtomicExchange32x2:
|
||||
case Opcode::GlobalAtomicIAdd32:
|
||||
case Opcode::GlobalAtomicSMin32:
|
||||
case Opcode::GlobalAtomicUMin32:
|
||||
case Opcode::GlobalAtomicSMax32:
|
||||
case Opcode::GlobalAtomicUMax32:
|
||||
case Opcode::GlobalAtomicInc32:
|
||||
case Opcode::GlobalAtomicDec32:
|
||||
case Opcode::GlobalAtomicAnd32:
|
||||
case Opcode::GlobalAtomicOr32:
|
||||
case Opcode::GlobalAtomicXor32:
|
||||
case Opcode::GlobalAtomicExchange32:
|
||||
case Opcode::GlobalAtomicIAdd64:
|
||||
case Opcode::GlobalAtomicSMin64:
|
||||
case Opcode::GlobalAtomicUMin64:
|
||||
case Opcode::GlobalAtomicSMax64:
|
||||
case Opcode::GlobalAtomicUMax64:
|
||||
case Opcode::GlobalAtomicAnd64:
|
||||
case Opcode::GlobalAtomicOr64:
|
||||
case Opcode::GlobalAtomicXor64:
|
||||
case Opcode::GlobalAtomicExchange64:
|
||||
case Opcode::GlobalAtomicIAdd32x2:
|
||||
case Opcode::GlobalAtomicSMin32x2:
|
||||
case Opcode::GlobalAtomicUMin32x2:
|
||||
case Opcode::GlobalAtomicSMax32x2:
|
||||
case Opcode::GlobalAtomicUMax32x2:
|
||||
case Opcode::GlobalAtomicAnd32x2:
|
||||
case Opcode::GlobalAtomicOr32x2:
|
||||
case Opcode::GlobalAtomicXor32x2:
|
||||
case Opcode::GlobalAtomicExchange32x2:
|
||||
case Opcode::GlobalAtomicAddF32:
|
||||
case Opcode::GlobalAtomicAddF16x2:
|
||||
case Opcode::GlobalAtomicAddF32x2:
|
||||
case Opcode::GlobalAtomicMinF16x2:
|
||||
case Opcode::GlobalAtomicMinF32x2:
|
||||
case Opcode::GlobalAtomicMaxF16x2:
|
||||
case Opcode::GlobalAtomicMaxF32x2:
|
||||
case Opcode::StorageAtomicIAdd32:
|
||||
case Opcode::StorageAtomicSMin32:
|
||||
case Opcode::StorageAtomicUMin32:
|
||||
case Opcode::StorageAtomicSMax32:
|
||||
case Opcode::StorageAtomicUMax32:
|
||||
case Opcode::StorageAtomicInc32:
|
||||
case Opcode::StorageAtomicDec32:
|
||||
case Opcode::StorageAtomicAnd32:
|
||||
case Opcode::StorageAtomicOr32:
|
||||
case Opcode::StorageAtomicXor32:
|
||||
case Opcode::StorageAtomicExchange32:
|
||||
case Opcode::StorageAtomicIAdd64:
|
||||
case Opcode::StorageAtomicSMin64:
|
||||
case Opcode::StorageAtomicUMin64:
|
||||
case Opcode::StorageAtomicSMax64:
|
||||
case Opcode::StorageAtomicUMax64:
|
||||
case Opcode::StorageAtomicAnd64:
|
||||
case Opcode::StorageAtomicOr64:
|
||||
case Opcode::StorageAtomicXor64:
|
||||
case Opcode::StorageAtomicExchange64:
|
||||
case Opcode::StorageAtomicIAdd32x2:
|
||||
case Opcode::StorageAtomicSMin32x2:
|
||||
case Opcode::StorageAtomicUMin32x2:
|
||||
case Opcode::StorageAtomicSMax32x2:
|
||||
case Opcode::StorageAtomicUMax32x2:
|
||||
case Opcode::StorageAtomicAnd32x2:
|
||||
case Opcode::StorageAtomicOr32x2:
|
||||
case Opcode::StorageAtomicXor32x2:
|
||||
case Opcode::StorageAtomicExchange32x2:
|
||||
case Opcode::StorageAtomicAddF32:
|
||||
case Opcode::StorageAtomicAddF16x2:
|
||||
case Opcode::StorageAtomicAddF32x2:
|
||||
case Opcode::StorageAtomicMinF16x2:
|
||||
case Opcode::StorageAtomicMinF32x2:
|
||||
case Opcode::StorageAtomicMaxF16x2:
|
||||
case Opcode::StorageAtomicMaxF32x2:
|
||||
case Opcode::BindlessImageWrite:
|
||||
case Opcode::BoundImageWrite:
|
||||
case Opcode::ImageWrite:
|
||||
case IR::Opcode::BindlessImageAtomicIAdd32:
|
||||
case IR::Opcode::BindlessImageAtomicSMin32:
|
||||
case IR::Opcode::BindlessImageAtomicUMin32:
|
||||
case IR::Opcode::BindlessImageAtomicSMax32:
|
||||
case IR::Opcode::BindlessImageAtomicUMax32:
|
||||
case IR::Opcode::BindlessImageAtomicInc32:
|
||||
case IR::Opcode::BindlessImageAtomicDec32:
|
||||
case IR::Opcode::BindlessImageAtomicAnd32:
|
||||
case IR::Opcode::BindlessImageAtomicOr32:
|
||||
case IR::Opcode::BindlessImageAtomicXor32:
|
||||
case IR::Opcode::BindlessImageAtomicExchange32:
|
||||
case IR::Opcode::BoundImageAtomicIAdd32:
|
||||
case IR::Opcode::BoundImageAtomicSMin32:
|
||||
case IR::Opcode::BoundImageAtomicUMin32:
|
||||
case IR::Opcode::BoundImageAtomicSMax32:
|
||||
case IR::Opcode::BoundImageAtomicUMax32:
|
||||
case IR::Opcode::BoundImageAtomicInc32:
|
||||
case IR::Opcode::BoundImageAtomicDec32:
|
||||
case IR::Opcode::BoundImageAtomicAnd32:
|
||||
case IR::Opcode::BoundImageAtomicOr32:
|
||||
case IR::Opcode::BoundImageAtomicXor32:
|
||||
case IR::Opcode::BoundImageAtomicExchange32:
|
||||
case IR::Opcode::ImageAtomicIAdd32:
|
||||
case IR::Opcode::ImageAtomicSMin32:
|
||||
case IR::Opcode::ImageAtomicUMin32:
|
||||
case IR::Opcode::ImageAtomicSMax32:
|
||||
case IR::Opcode::ImageAtomicUMax32:
|
||||
case IR::Opcode::ImageAtomicInc32:
|
||||
case IR::Opcode::ImageAtomicDec32:
|
||||
case IR::Opcode::ImageAtomicAnd32:
|
||||
case IR::Opcode::ImageAtomicOr32:
|
||||
case IR::Opcode::ImageAtomicXor32:
|
||||
case IR::Opcode::ImageAtomicExchange32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::IsPseudoInstruction() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
case Opcode::GetSignFromOp:
|
||||
case Opcode::GetCarryFromOp:
|
||||
case Opcode::GetOverflowFromOp:
|
||||
case Opcode::GetSparseFromOp:
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::AreAllArgsImmediates() const {
|
||||
if (op == Opcode::Phi) {
|
||||
throw LogicError("Testing for all arguments are immediates on phi instruction");
|
||||
}
|
||||
return std::all_of(args.begin(), args.begin() + NumArgs(),
|
||||
[](const IR::Value& value) { return value.IsImmediate(); });
|
||||
}
|
||||
|
||||
Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
|
||||
if (!associated_insts) {
|
||||
return nullptr;
|
||||
}
|
||||
switch (opcode) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
CheckPseudoInstruction(associated_insts->zero_inst, Opcode::GetZeroFromOp);
|
||||
return associated_insts->zero_inst;
|
||||
case Opcode::GetSignFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sign_inst, Opcode::GetSignFromOp);
|
||||
return associated_insts->sign_inst;
|
||||
case Opcode::GetCarryFromOp:
|
||||
CheckPseudoInstruction(associated_insts->carry_inst, Opcode::GetCarryFromOp);
|
||||
return associated_insts->carry_inst;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
CheckPseudoInstruction(associated_insts->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
return associated_insts->overflow_inst;
|
||||
case Opcode::GetSparseFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sparse_inst, Opcode::GetSparseFromOp);
|
||||
return associated_insts->sparse_inst;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
CheckPseudoInstruction(associated_insts->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
return associated_insts->in_bounds_inst;
|
||||
default:
|
||||
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Type Inst::Type() const {
|
||||
if (op == IR::Opcode::Phi) {
|
||||
// The type of a phi node is stored in its flags
|
||||
return Flags<IR::Type>();
|
||||
}
|
||||
return TypeOf(op);
|
||||
}
|
||||
|
||||
void Inst::SetArg(size_t index, Value value) {
|
||||
if (index >= NumArgs()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
|
||||
}
|
||||
const IR::Value arg{Arg(index)};
|
||||
if (!arg.IsImmediate()) {
|
||||
UndoUse(arg);
|
||||
}
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
phi_args[index].second = value;
|
||||
} else {
|
||||
args[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Block* Inst::PhiBlock(size_t index) const {
|
||||
if (op != Opcode::Phi) {
|
||||
throw LogicError("{} is not a Phi instruction", op);
|
||||
}
|
||||
if (index >= phi_args.size()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in phi instruction");
|
||||
}
|
||||
return phi_args[index].first;
|
||||
}
|
||||
|
||||
void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
phi_args.emplace_back(predecessor, value);
|
||||
}
|
||||
|
||||
void Inst::OrderPhiArgs() {
|
||||
if (op != Opcode::Phi) {
|
||||
throw LogicError("{} is not a Phi instruction", op);
|
||||
}
|
||||
std::sort(phi_args.begin(), phi_args.end(),
|
||||
[](const std::pair<Block*, Value>& a, const std::pair<Block*, Value>& b) {
|
||||
return a.first->GetOrder() < b.first->GetOrder();
|
||||
});
|
||||
}
|
||||
|
||||
void Inst::Invalidate() {
|
||||
ClearArgs();
|
||||
ReplaceOpcode(Opcode::Void);
|
||||
}
|
||||
|
||||
void Inst::ClearArgs() {
|
||||
if (op == Opcode::Phi) {
|
||||
for (auto& pair : phi_args) {
|
||||
IR::Value& value{pair.second};
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
phi_args.clear();
|
||||
} else {
|
||||
for (auto& value : args) {
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
// Reset arguments to null
|
||||
// std::memset was measured to be faster on MSVC than std::ranges:fill
|
||||
std::memset(reinterpret_cast<char*>(&args), 0, sizeof(args));
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::ReplaceUsesWith(Value replacement) {
|
||||
Invalidate();
|
||||
ReplaceOpcode(Opcode::Identity);
|
||||
if (!replacement.IsImmediate()) {
|
||||
Use(replacement);
|
||||
}
|
||||
args[0] = replacement;
|
||||
}
|
||||
|
||||
void Inst::ReplaceOpcode(IR::Opcode opcode) {
|
||||
if (opcode == IR::Opcode::Phi) {
|
||||
throw LogicError("Cannot transition into Phi");
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
// Transition out of phi arguments into non-phi
|
||||
std::destroy_at(&phi_args);
|
||||
std::construct_at(&args);
|
||||
}
|
||||
op = opcode;
|
||||
}
|
||||
|
||||
void Inst::Use(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
++inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->zero_inst, this);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sign_inst, this);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->carry_inst, this);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->overflow_inst, this);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sparse_inst, this);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->in_bounds_inst, this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::UndoUse(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
--inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->zero_inst, Opcode::GetZeroFromOp);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sign_inst, Opcode::GetSignFromOp);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->carry_inst, Opcode::GetCarryFromOp);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sparse_inst, Opcode::GetSparseFromOp);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FmzMode : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
FTZ, // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK)
|
||||
FMZ, // Flush denorms to zero, x * 0 == 0 (D3D9)
|
||||
None, // Denorms are not flushed, NAN is propagated (nouveau)
|
||||
};
|
||||
|
||||
enum class FpRounding : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
RN, // Round to nearest even,
|
||||
RM, // Round towards negative infinity
|
||||
RP, // Round towards positive infinity
|
||||
RZ, // Round towards zero
|
||||
};
|
||||
|
||||
struct FpControl {
|
||||
bool no_contraction{false};
|
||||
FpRounding rounding{FpRounding::DontCare};
|
||||
FmzMode fmz_mode{FmzMode::DontCare};
|
||||
};
|
||||
static_assert(sizeof(FpControl) <= sizeof(u32));
|
||||
|
||||
union TextureInstInfo {
|
||||
u32 raw;
|
||||
BitField<0, 16, u32> descriptor_index;
|
||||
BitField<16, 3, TextureType> type;
|
||||
BitField<19, 1, u32> is_depth;
|
||||
BitField<20, 1, u32> has_bias;
|
||||
BitField<21, 1, u32> has_lod_clamp;
|
||||
BitField<22, 1, u32> relaxed_precision;
|
||||
BitField<23, 2, u32> gather_component;
|
||||
BitField<25, 2, u32> num_derivates;
|
||||
BitField<27, 3, ImageFormat> image_format;
|
||||
};
|
||||
static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FmzMode : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
FTZ, // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK)
|
||||
FMZ, // Flush denorms to zero, x * 0 == 0 (D3D9)
|
||||
None, // Denorms are not flushed, NAN is propagated (nouveau)
|
||||
};
|
||||
|
||||
enum class FpRounding : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
RN, // Round to nearest even,
|
||||
RM, // Round towards negative infinity
|
||||
RP, // Round towards positive infinity
|
||||
RZ, // Round towards zero
|
||||
};
|
||||
|
||||
struct FpControl {
|
||||
bool no_contraction{false};
|
||||
FpRounding rounding{FpRounding::DontCare};
|
||||
FmzMode fmz_mode{FmzMode::DontCare};
|
||||
};
|
||||
static_assert(sizeof(FpControl) <= sizeof(u32));
|
||||
|
||||
union TextureInstInfo {
|
||||
u32 raw;
|
||||
BitField<0, 16, u32> descriptor_index;
|
||||
BitField<16, 3, TextureType> type;
|
||||
BitField<19, 1, u32> is_depth;
|
||||
BitField<20, 1, u32> has_bias;
|
||||
BitField<21, 1, u32> has_lod_clamp;
|
||||
BitField<22, 1, u32> relaxed_precision;
|
||||
BitField<23, 2, u32> gather_component;
|
||||
BitField<25, 2, u32> num_derivates;
|
||||
BitField<27, 3, ImageFormat> image_format;
|
||||
};
|
||||
static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/frontend/ir/opcodes.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string_view NameOf(Opcode op) {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].name;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/frontend/ir/opcodes.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string_view NameOf(Opcode op) {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].name;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,108 +1,108 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Opcode {
|
||||
#define OPCODE(name, ...) name,
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
namespace Detail {
|
||||
struct OpcodeMeta {
|
||||
std::string_view name;
|
||||
Type type;
|
||||
std::array<Type, 5> arg_types;
|
||||
};
|
||||
|
||||
// using enum Type;
|
||||
constexpr Type Void{Type::Void};
|
||||
constexpr Type Opaque{Type::Opaque};
|
||||
constexpr Type Reg{Type::Reg};
|
||||
constexpr Type Pred{Type::Pred};
|
||||
constexpr Type Attribute{Type::Attribute};
|
||||
constexpr Type Patch{Type::Patch};
|
||||
constexpr Type U1{Type::U1};
|
||||
constexpr Type U8{Type::U8};
|
||||
constexpr Type U16{Type::U16};
|
||||
constexpr Type U32{Type::U32};
|
||||
constexpr Type U64{Type::U64};
|
||||
constexpr Type F16{Type::F16};
|
||||
constexpr Type F32{Type::F32};
|
||||
constexpr Type F64{Type::F64};
|
||||
constexpr Type U32x2{Type::U32x2};
|
||||
constexpr Type U32x3{Type::U32x3};
|
||||
constexpr Type U32x4{Type::U32x4};
|
||||
constexpr Type F16x2{Type::F16x2};
|
||||
constexpr Type F16x3{Type::F16x3};
|
||||
constexpr Type F16x4{Type::F16x4};
|
||||
constexpr Type F32x2{Type::F32x2};
|
||||
constexpr Type F32x3{Type::F32x3};
|
||||
constexpr Type F32x4{Type::F32x4};
|
||||
constexpr Type F64x2{Type::F64x2};
|
||||
constexpr Type F64x3{Type::F64x3};
|
||||
constexpr Type F64x4{Type::F64x4};
|
||||
|
||||
constexpr OpcodeMeta META_TABLE[]{
|
||||
#define OPCODE(name_token, type_token, ...) \
|
||||
{ \
|
||||
.name{#name_token}, \
|
||||
.type = type_token, \
|
||||
.arg_types{__VA_ARGS__}, \
|
||||
},
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
constexpr size_t CalculateNumArgsOf(Opcode op) {
|
||||
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
|
||||
return static_cast<size_t>(
|
||||
std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
|
||||
}
|
||||
|
||||
constexpr u8 NUM_ARGS[]{
|
||||
#define OPCODE(name_token, type_token, ...) static_cast<u8>(CalculateNumArgsOf(Opcode::name_token)),
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
/// Get return type of an opcode
|
||||
[[nodiscard]] inline Type TypeOf(Opcode op) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].type;
|
||||
}
|
||||
|
||||
/// Get the number of arguments an opcode accepts
|
||||
[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept {
|
||||
return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]);
|
||||
}
|
||||
|
||||
/// Get the required type of an argument of an opcode
|
||||
[[nodiscard]] inline Type ArgTypeOf(Opcode op, size_t arg_index) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].arg_types[arg_index];
|
||||
}
|
||||
|
||||
/// Get the name of an opcode
|
||||
[[nodiscard]] std::string_view NameOf(Opcode op);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Opcode> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Opcode {
|
||||
#define OPCODE(name, ...) name,
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
namespace Detail {
|
||||
struct OpcodeMeta {
|
||||
std::string_view name;
|
||||
Type type;
|
||||
std::array<Type, 5> arg_types;
|
||||
};
|
||||
|
||||
// using enum Type;
|
||||
constexpr Type Void{Type::Void};
|
||||
constexpr Type Opaque{Type::Opaque};
|
||||
constexpr Type Reg{Type::Reg};
|
||||
constexpr Type Pred{Type::Pred};
|
||||
constexpr Type Attribute{Type::Attribute};
|
||||
constexpr Type Patch{Type::Patch};
|
||||
constexpr Type U1{Type::U1};
|
||||
constexpr Type U8{Type::U8};
|
||||
constexpr Type U16{Type::U16};
|
||||
constexpr Type U32{Type::U32};
|
||||
constexpr Type U64{Type::U64};
|
||||
constexpr Type F16{Type::F16};
|
||||
constexpr Type F32{Type::F32};
|
||||
constexpr Type F64{Type::F64};
|
||||
constexpr Type U32x2{Type::U32x2};
|
||||
constexpr Type U32x3{Type::U32x3};
|
||||
constexpr Type U32x4{Type::U32x4};
|
||||
constexpr Type F16x2{Type::F16x2};
|
||||
constexpr Type F16x3{Type::F16x3};
|
||||
constexpr Type F16x4{Type::F16x4};
|
||||
constexpr Type F32x2{Type::F32x2};
|
||||
constexpr Type F32x3{Type::F32x3};
|
||||
constexpr Type F32x4{Type::F32x4};
|
||||
constexpr Type F64x2{Type::F64x2};
|
||||
constexpr Type F64x3{Type::F64x3};
|
||||
constexpr Type F64x4{Type::F64x4};
|
||||
|
||||
constexpr OpcodeMeta META_TABLE[]{
|
||||
#define OPCODE(name_token, type_token, ...) \
|
||||
{ \
|
||||
.name{#name_token}, \
|
||||
.type = type_token, \
|
||||
.arg_types{__VA_ARGS__}, \
|
||||
},
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
constexpr size_t CalculateNumArgsOf(Opcode op) {
|
||||
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
|
||||
return static_cast<size_t>(
|
||||
std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
|
||||
}
|
||||
|
||||
constexpr u8 NUM_ARGS[]{
|
||||
#define OPCODE(name_token, type_token, ...) static_cast<u8>(CalculateNumArgsOf(Opcode::name_token)),
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
/// Get return type of an opcode
|
||||
[[nodiscard]] inline Type TypeOf(Opcode op) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].type;
|
||||
}
|
||||
|
||||
/// Get the number of arguments an opcode accepts
|
||||
[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept {
|
||||
return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]);
|
||||
}
|
||||
|
||||
/// Get the required type of an argument of an opcode
|
||||
[[nodiscard]] inline Type ArgTypeOf(Opcode op, size_t arg_index) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].arg_types[arg_index];
|
||||
}
|
||||
|
||||
/// Get the name of an opcode
|
||||
[[nodiscard]] std::string_view NameOf(Opcode op);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Opcode> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/patch.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Patch patch) noexcept {
|
||||
return patch >= Patch::Component0 && patch <= Patch::Component119;
|
||||
}
|
||||
|
||||
u32 GenericPatchIndex(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4;
|
||||
}
|
||||
|
||||
u32 GenericPatchElement(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/patch.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Patch patch) noexcept {
|
||||
return patch >= Patch::Component0 && patch <= Patch::Component119;
|
||||
}
|
||||
|
||||
u32 GenericPatchIndex(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4;
|
||||
}
|
||||
|
||||
u32 GenericPatchElement(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,148 +1,148 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Patch : u64 {
|
||||
TessellationLodLeft,
|
||||
TessellationLodTop,
|
||||
TessellationLodRight,
|
||||
TessellationLodBottom,
|
||||
TessellationLodInteriorU,
|
||||
TessellationLodInteriorV,
|
||||
ComponentPadding0,
|
||||
ComponentPadding1,
|
||||
Component0,
|
||||
Component1,
|
||||
Component2,
|
||||
Component3,
|
||||
Component4,
|
||||
Component5,
|
||||
Component6,
|
||||
Component7,
|
||||
Component8,
|
||||
Component9,
|
||||
Component10,
|
||||
Component11,
|
||||
Component12,
|
||||
Component13,
|
||||
Component14,
|
||||
Component15,
|
||||
Component16,
|
||||
Component17,
|
||||
Component18,
|
||||
Component19,
|
||||
Component20,
|
||||
Component21,
|
||||
Component22,
|
||||
Component23,
|
||||
Component24,
|
||||
Component25,
|
||||
Component26,
|
||||
Component27,
|
||||
Component28,
|
||||
Component29,
|
||||
Component30,
|
||||
Component31,
|
||||
Component32,
|
||||
Component33,
|
||||
Component34,
|
||||
Component35,
|
||||
Component36,
|
||||
Component37,
|
||||
Component38,
|
||||
Component39,
|
||||
Component40,
|
||||
Component41,
|
||||
Component42,
|
||||
Component43,
|
||||
Component44,
|
||||
Component45,
|
||||
Component46,
|
||||
Component47,
|
||||
Component48,
|
||||
Component49,
|
||||
Component50,
|
||||
Component51,
|
||||
Component52,
|
||||
Component53,
|
||||
Component54,
|
||||
Component55,
|
||||
Component56,
|
||||
Component57,
|
||||
Component58,
|
||||
Component59,
|
||||
Component60,
|
||||
Component61,
|
||||
Component62,
|
||||
Component63,
|
||||
Component64,
|
||||
Component65,
|
||||
Component66,
|
||||
Component67,
|
||||
Component68,
|
||||
Component69,
|
||||
Component70,
|
||||
Component71,
|
||||
Component72,
|
||||
Component73,
|
||||
Component74,
|
||||
Component75,
|
||||
Component76,
|
||||
Component77,
|
||||
Component78,
|
||||
Component79,
|
||||
Component80,
|
||||
Component81,
|
||||
Component82,
|
||||
Component83,
|
||||
Component84,
|
||||
Component85,
|
||||
Component86,
|
||||
Component87,
|
||||
Component88,
|
||||
Component89,
|
||||
Component90,
|
||||
Component91,
|
||||
Component92,
|
||||
Component93,
|
||||
Component94,
|
||||
Component95,
|
||||
Component96,
|
||||
Component97,
|
||||
Component98,
|
||||
Component99,
|
||||
Component100,
|
||||
Component101,
|
||||
Component102,
|
||||
Component103,
|
||||
Component104,
|
||||
Component105,
|
||||
Component106,
|
||||
Component107,
|
||||
Component108,
|
||||
Component109,
|
||||
Component110,
|
||||
Component111,
|
||||
Component112,
|
||||
Component113,
|
||||
Component114,
|
||||
Component115,
|
||||
Component116,
|
||||
Component117,
|
||||
Component118,
|
||||
Component119,
|
||||
};
|
||||
static_assert(static_cast<u64>(Patch::Component119) == 127);
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericPatchIndex(Patch patch);
|
||||
|
||||
[[nodiscard]] u32 GenericPatchElement(Patch patch);
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Patch : u64 {
|
||||
TessellationLodLeft,
|
||||
TessellationLodTop,
|
||||
TessellationLodRight,
|
||||
TessellationLodBottom,
|
||||
TessellationLodInteriorU,
|
||||
TessellationLodInteriorV,
|
||||
ComponentPadding0,
|
||||
ComponentPadding1,
|
||||
Component0,
|
||||
Component1,
|
||||
Component2,
|
||||
Component3,
|
||||
Component4,
|
||||
Component5,
|
||||
Component6,
|
||||
Component7,
|
||||
Component8,
|
||||
Component9,
|
||||
Component10,
|
||||
Component11,
|
||||
Component12,
|
||||
Component13,
|
||||
Component14,
|
||||
Component15,
|
||||
Component16,
|
||||
Component17,
|
||||
Component18,
|
||||
Component19,
|
||||
Component20,
|
||||
Component21,
|
||||
Component22,
|
||||
Component23,
|
||||
Component24,
|
||||
Component25,
|
||||
Component26,
|
||||
Component27,
|
||||
Component28,
|
||||
Component29,
|
||||
Component30,
|
||||
Component31,
|
||||
Component32,
|
||||
Component33,
|
||||
Component34,
|
||||
Component35,
|
||||
Component36,
|
||||
Component37,
|
||||
Component38,
|
||||
Component39,
|
||||
Component40,
|
||||
Component41,
|
||||
Component42,
|
||||
Component43,
|
||||
Component44,
|
||||
Component45,
|
||||
Component46,
|
||||
Component47,
|
||||
Component48,
|
||||
Component49,
|
||||
Component50,
|
||||
Component51,
|
||||
Component52,
|
||||
Component53,
|
||||
Component54,
|
||||
Component55,
|
||||
Component56,
|
||||
Component57,
|
||||
Component58,
|
||||
Component59,
|
||||
Component60,
|
||||
Component61,
|
||||
Component62,
|
||||
Component63,
|
||||
Component64,
|
||||
Component65,
|
||||
Component66,
|
||||
Component67,
|
||||
Component68,
|
||||
Component69,
|
||||
Component70,
|
||||
Component71,
|
||||
Component72,
|
||||
Component73,
|
||||
Component74,
|
||||
Component75,
|
||||
Component76,
|
||||
Component77,
|
||||
Component78,
|
||||
Component79,
|
||||
Component80,
|
||||
Component81,
|
||||
Component82,
|
||||
Component83,
|
||||
Component84,
|
||||
Component85,
|
||||
Component86,
|
||||
Component87,
|
||||
Component88,
|
||||
Component89,
|
||||
Component90,
|
||||
Component91,
|
||||
Component92,
|
||||
Component93,
|
||||
Component94,
|
||||
Component95,
|
||||
Component96,
|
||||
Component97,
|
||||
Component98,
|
||||
Component99,
|
||||
Component100,
|
||||
Component101,
|
||||
Component102,
|
||||
Component103,
|
||||
Component104,
|
||||
Component105,
|
||||
Component106,
|
||||
Component107,
|
||||
Component108,
|
||||
Component109,
|
||||
Component110,
|
||||
Component111,
|
||||
Component112,
|
||||
Component113,
|
||||
Component114,
|
||||
Component115,
|
||||
Component116,
|
||||
Component117,
|
||||
Component118,
|
||||
Component119,
|
||||
};
|
||||
static_assert(static_cast<u64>(Patch::Component119) == 127);
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericPatchIndex(Patch patch);
|
||||
|
||||
[[nodiscard]] u32 GenericPatchElement(Patch patch);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/post_order.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root) {
|
||||
boost::container::small_vector<Block*, 16> block_stack;
|
||||
boost::container::flat_set<Block*> visited;
|
||||
BlockList post_order_blocks;
|
||||
|
||||
if (root.type != AbstractSyntaxNode::Type::Block) {
|
||||
throw LogicError("First node in abstract syntax list root is not a block");
|
||||
}
|
||||
Block* const first_block{root.data.block};
|
||||
visited.insert(first_block);
|
||||
block_stack.push_back(first_block);
|
||||
|
||||
while (!block_stack.empty()) {
|
||||
Block* const block{block_stack.back()};
|
||||
const auto visit{[&](Block* branch) {
|
||||
if (!visited.insert(branch).second) {
|
||||
return false;
|
||||
}
|
||||
// Calling push_back twice is faster than insert on MSVC
|
||||
block_stack.push_back(block);
|
||||
block_stack.push_back(branch);
|
||||
return true;
|
||||
}};
|
||||
block_stack.pop_back();
|
||||
if (std::ranges::none_of(block->ImmSuccessors(), visit)) {
|
||||
post_order_blocks.push_back(block);
|
||||
}
|
||||
}
|
||||
return post_order_blocks;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/post_order.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root) {
|
||||
boost::container::small_vector<Block*, 16> block_stack;
|
||||
boost::container::flat_set<Block*> visited;
|
||||
BlockList post_order_blocks;
|
||||
|
||||
if (root.type != AbstractSyntaxNode::Type::Block) {
|
||||
throw LogicError("First node in abstract syntax list root is not a block");
|
||||
}
|
||||
Block* const first_block{root.data.block};
|
||||
visited.insert(first_block);
|
||||
block_stack.push_back(first_block);
|
||||
|
||||
while (!block_stack.empty()) {
|
||||
Block* const block{block_stack.back()};
|
||||
const auto visit{[&](Block* branch) {
|
||||
if (!visited.insert(branch).second) {
|
||||
return false;
|
||||
}
|
||||
// Calling push_back twice is faster than insert on MSVC
|
||||
block_stack.push_back(block);
|
||||
block_stack.push_back(branch);
|
||||
return true;
|
||||
}};
|
||||
block_stack.pop_back();
|
||||
if (std::ranges::none_of(block->ImmSuccessors(), visit)) {
|
||||
post_order_blocks.push_back(block);
|
||||
}
|
||||
}
|
||||
return post_order_blocks;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root);
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Pred : u64 {
|
||||
P0,
|
||||
P1,
|
||||
P2,
|
||||
P3,
|
||||
P4,
|
||||
P5,
|
||||
P6,
|
||||
PT,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_USER_PREDS = 7;
|
||||
constexpr size_t NUM_PREDS = 8;
|
||||
|
||||
[[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept {
|
||||
return static_cast<size_t>(pred);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Pred> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Pred& pred, FormatContext& ctx) {
|
||||
if (pred == Shader::IR::Pred::PT) {
|
||||
return fmt::format_to(ctx.out(), "PT");
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred));
|
||||
}
|
||||
}
|
||||
};
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Pred : u64 {
|
||||
P0,
|
||||
P1,
|
||||
P2,
|
||||
P3,
|
||||
P4,
|
||||
P5,
|
||||
P6,
|
||||
PT,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_USER_PREDS = 7;
|
||||
constexpr size_t NUM_PREDS = 8;
|
||||
|
||||
[[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept {
|
||||
return static_cast<size_t>(pred);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Pred> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Pred& pred, FormatContext& ctx) {
|
||||
if (pred == Shader::IR::Pred::PT) {
|
||||
return fmt::format_to(ctx.out(), "PT");
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string DumpProgram(const Program& program) {
|
||||
size_t index{0};
|
||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||
std::map<const IR::Block*, size_t> block_to_index;
|
||||
|
||||
for (const IR::Block* const block : program.blocks) {
|
||||
block_to_index.emplace(block, index);
|
||||
++index;
|
||||
}
|
||||
std::string ret;
|
||||
for (const auto& block : program.blocks) {
|
||||
ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string DumpProgram(const Program& program) {
|
||||
size_t index{0};
|
||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||
std::map<const IR::Block*, size_t> block_to_index;
|
||||
|
||||
for (const IR::Block* const block : program.blocks) {
|
||||
block_to_index.emplace(block, index);
|
||||
++index;
|
||||
}
|
||||
std::string ret;
|
||||
for (const auto& block : program.blocks) {
|
||||
ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
struct Program {
|
||||
AbstractSyntaxList syntax_list;
|
||||
BlockList blocks;
|
||||
BlockList post_order_blocks;
|
||||
Info info;
|
||||
Stage stage{};
|
||||
std::array<u32, 3> workgroup_size{};
|
||||
OutputTopology output_topology{};
|
||||
u32 output_vertices{};
|
||||
u32 invocations{};
|
||||
u32 local_memory_size{};
|
||||
u32 shared_memory_size{};
|
||||
bool is_geometry_passthrough{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string DumpProgram(const Program& program);
|
||||
|
||||
} // namespace Shader::IR
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
struct Program {
|
||||
AbstractSyntaxList syntax_list;
|
||||
BlockList blocks;
|
||||
BlockList post_order_blocks;
|
||||
Info info;
|
||||
Stage stage{};
|
||||
std::array<u32, 3> workgroup_size{};
|
||||
OutputTopology output_topology{};
|
||||
u32 output_vertices{};
|
||||
u32 invocations{};
|
||||
u32 local_memory_size{};
|
||||
u32 shared_memory_size{};
|
||||
bool is_geometry_passthrough{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string DumpProgram(const Program& program);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user