Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,11 @@ target_link_libraries(agc_fma_mix_once_tests PRIVATE shader_recompiler_agc_drive
set_target_properties(agc_fma_mix_once_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_fma_mix_once_tests)

add_test_executable(agc_f16_single_convert_tests EXCLUDE_FROM_ALL tests/F16SingleConvert.cpp)
target_link_libraries(agc_f16_single_convert_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_f16_single_convert_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_f16_single_convert_tests)

add_test_executable(agc_rect_list_position_invariant_tests EXCLUDE_FROM_ALL tests/RectListPositionInvariant.cpp)
target_link_libraries(agc_rect_list_position_invariant_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_rect_list_position_invariant_tests PROPERTIES CXX_EXTENSIONS OFF)
Expand Down Expand Up @@ -1817,7 +1822,7 @@ if(BUILD_TESTING)
if(WIN32)
set_tests_properties(agc_shader_bindless agc_shader_selected_records_image_limit agc_shader_selected_records_pair_limit agc_shader_selected_records_direct_image_limit agc_shader_selected_records_direct_pair_limit agc_shader_srt_read_patching agc_shader_type_zero_image_materialization agc_shader_frontend_pair agc_shader_frontend_pair_off agc_shader_layout_pair agc_shader_layout_pair_off agc_shader_entry_lane_proof_budget PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(agcTest IN ITEMS agc_vertex_input_tests agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_ir_reclaim_tests agc_vector_window_tests agc_constant_bit_test_tests agc_integer_narrowing_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests agc_formatted_buffer_byte_stores_tests ${agcExecutionTests} agc_fma_mix_once_tests)
foreach(agcTest IN ITEMS agc_vertex_input_tests agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_ir_reclaim_tests agc_vector_window_tests agc_constant_bit_test_tests agc_integer_narrowing_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests agc_formatted_buffer_byte_stores_tests ${agcExecutionTests} agc_fma_mix_once_tests agc_f16_single_convert_tests)
if(agcTest STREQUAL "agc_driver_constant_store_tests" OR agcTest STREQUAL "agc_driver_flat_apertures_tests" OR agcTest STREQUAL "agc_driver_pipeline_optimized_readback_tests")
continue()
endif()
Expand Down
93 changes: 93 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/F16SingleConvert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "BdaAbi.hpp"
#include "Recompiler.hpp"
#include <array>
#include <cstdio>
#include <exception>
#include <initializer_list>
#include <span>
#include <spirv/unified1/spirv.hpp>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace {

using namespace ShaderRecompiler;

void Check(bool condition, const std::string& message) {
if (!condition) throw std::runtime_error(message);
}

SpirvTarget Target(bool nativeFloat16) {
static constexpr std::array<std::uint32_t, 5> plain{
spv::CapabilityInt64, spv::CapabilityFloat64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess, spv::CapabilityClipDistance,
};
static constexpr std::array<std::uint32_t, 8> native{
spv::CapabilityInt64, spv::CapabilityFloat64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess, spv::CapabilityClipDistance,
spv::CapabilityFloat16, spv::CapabilityRoundingModeRTE, spv::CapabilityDenormPreserve,
};
static constexpr std::array<std::string_view, 4> extensions{
"SPV_EXT_descriptor_indexing", "SPV_KHR_8bit_storage", "SPV_KHR_float_controls", "SPV_KHR_physical_storage_buffer",
};
if (nativeFloat16) return {0x00401000u, 0x00010300u, 32, BdaAbi::Version, native, extensions, false, {1024, 1024, 64}, 1024, 32768, {}, {}};
return {0x00401000u, 0x00010300u, 32, BdaAbi::Version, plain, extensions, false, {1024, 1024, 64}, 1024, 32768, {}, {}};
}

std::size_t ZeroPairs(std::span<const std::uint32_t> words) {
Check(words.size() >= 5 && words[0] == spv::MagicNumber, "f16 single convert: missing SPIR-V module");
std::unordered_set<std::uint32_t> floats;
std::unordered_set<std::uint32_t> pairs;
std::unordered_set<std::uint32_t> zeros;
std::size_t count = 0;
for (std::size_t offset = 5; offset < words.size();) {
const auto length = words[offset] >> spv::WordCountShift;
Check(length != 0 && length <= words.size() - offset, "f16 single convert: malformed SPIR-V instruction");
const auto opcode = words[offset] & spv::OpCodeMask;
if (opcode == spv::OpTypeFloat && length == 3u && words[offset + 2u] == 32u) floats.insert(words[offset + 1u]);
if (opcode == spv::OpTypeVector && length == 4u && floats.contains(words[offset + 2u]) && words[offset + 3u] == 2u) pairs.insert(words[offset + 1u]);
if (opcode == spv::OpConstant && length == 4u && floats.contains(words[offset + 1u]) && words[offset + 3u] == 0u) zeros.insert(words[offset + 2u]);
if (opcode == spv::OpConstantNull && length == 3u && floats.contains(words[offset + 1u])) zeros.insert(words[offset + 2u]);
if (opcode == spv::OpCompositeConstruct && length == 5u && pairs.contains(words[offset + 1u]) && zeros.contains(words[offset + 4u])) ++count;
offset += length;
}
return count;
}

std::size_t Compile(bool nativeFloat16) {
std::vector<std::uint32_t> code{0xe0300000u, 0x80010100u, 0xbf8c3f70u};
for (std::uint32_t op = 0; op < 4u; ++op) code.push_back(0x7e04a900u | (256u + (op == 0u ? 1u : 2u)));
code.insert(code.end(), {0xe0700000u, 0x80010200u, 0xbf810000u});
const std::span<const std::uint32_t> words(code);
const std::array<MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(words)}}};
const std::array<std::uint32_t, 8> userData{0u, 0u, 0u, 0u, 0x10000000u, 0u, 0x1000u, 0x31016facu};
RecompileRequest request{};
request.shader = {ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), words, 0, {}};
request.target = Target(nativeFloat16);
request.layout = {0, 0, 0, 128};
request.context.waveSize = 32;
request.context.userDataBaseRegister = 0;
request.context.userData = userData;
request.context.compute = ShaderComputeStageInfo{{32u, 1u, 1u}, 0u, {false, false, false}, false, 1u};
request.context.memory = memory;
request.useCache = false;
return ZeroPairs(Recompile(request).spirv.Words());
}

}

int main() {
try {
for (const bool nativeFloat16 : {false, true}) {
const auto pairs = Compile(nativeFloat16);
Check(pairs == 0u, std::string("f16 single convert: four v_rcp_f16 emitted ") + std::to_string(pairs) + " (x, 0.0) f32 pairs to convert one f16 each" + (nativeFloat16 ? " with native f16" : " without native f16"));
}
std::puts("f16 single convert tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,7 @@ std::uint32_t EmitConvertU8U32(SpirvEmitterState& state, std::uint32_t arg0) {
}

std::uint32_t EmitConvertF16F32(SpirvEmitterState& state, std::uint32_t arg0) {
const auto pair = state.module.AllocateId();
state.module.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 2), pair, arg0, ConstantF32(state, 0u));
return EmitPackHalf2x16(state, pair);
return EmitF32ToF16BitsRte(state, arg0);
}

std::uint32_t EmitConvertS32F32(SpirvEmitterState& state, std::uint32_t arg0) {
Expand Down
3 changes: 2 additions & 1 deletion core/shader/recompiler/Translation/src/FloatInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ bool TranslationContext::float16Unary(const RdnaInstruction& inst, IrOpcode opco
const RdnaOperand& operand = sourceAt(inst, 0u);
const IrF32 argument = flushF16InputValue(readF16AsF32(operand));
const auto [result, invalid] = unaryFloatSpecials(opcode, argument, IrF32(ir.Emit(opcode, IrType::F32, {&argument.Value()})));
IrU32 bits = packHalf2x16(applyF16ResultModifiers(inst.destination, clampF16Overflow(result, {&argument.Value()})), IrF32(ir.ConstantF32(0.0f)));
const IrF16 rounded(ir.Emit(IrOpcode::ConvertF16F32, IrType::F16, {&applyF16ResultModifiers(inst.destination, clampF16Overflow(result, {&argument.Value()})).Value()}));
IrU32 bits(ir.Emit(IrOpcode::ConvertU32U16, IrType::U32, {&ir.Emit(IrOpcode::BitCastU16F16, IrType::U16, {&rounded.Value()})}));
if (opcode == IrOpcode::FPFract32) {
bits = IrU32(ir.Select(ir.IEqual(bits.Value(), ir.Constant(0x3c00u)), ir.Constant(0x3bffu), bits.Value()));
}
Expand Down
Loading