shader: Add glsl compiler.

This commit is contained in:
pent0
2022-05-20 15:09:30 +02:00
committed by Zangetsu38
parent df1378486d
commit 941791b1d4
61 changed files with 9872 additions and 2372 deletions
+1
View File
@@ -24,6 +24,7 @@ struct FeatureState {
bool spirv_shader = false;
bool preserve_f16_nan_as_u16 = true; ///< Emit store of 4xU16 to draw buffer 1. This buffer is expected to be U16U16U16U16, which can be casted to F16F16F16F16. This is to preserve some drivers's behaviour of casting NaN to default value when store in framebuffer, not keeping its original value.
bool support_get_texture_sub_image = false;
bool support_glsl_mixing_integers = false;
bool is_programmable_blending_supported() const {
return support_shader_interlock || support_texture_barrier || direct_fragcolor;
+1 -1
View File
@@ -28,7 +28,7 @@
#include <modules/module_parent.h>
#include <renderer/functions.h>
#include <renderer/gl/functions.h>
#include <shader/spirv_recompiler.h>
#include <shader/recompiler.h>
#include <util/log.h>
#include <util/string_utils.h>
+1 -1
View File
@@ -24,7 +24,7 @@
#include <renderer/gl/ring_buffer.h>
#include <renderer/texture_cache_state.h>
#include <shader/usse_program_analyzer.h>
#include <shader/program_analyzer.h>
#include <map>
#include <memory>
+1 -1
View File
@@ -24,7 +24,7 @@
#include <gxm/types.h>
#include <util/log.h>
#include <shader/spirv_recompiler.h>
#include <shader/recompiler.h>
#include <gxm/functions.h>
#include <vector>
+1 -1
View File
@@ -29,7 +29,7 @@
#include <gxm/types.h>
#include <util/log.h>
#include <shader/spirv_recompiler.h>
#include <shader/recompiler.h>
#include <features/state.h>
#include <gxm/functions.h>
+1 -1
View File
@@ -22,7 +22,7 @@
#include <gxm/types.h>
#include <renderer/types.h>
#include <shader/spirv_recompiler.h>
#include <shader/recompiler.h>
#include <util/fs.h>
#include <util/log.h>
+2 -2
View File
@@ -23,8 +23,8 @@
#include <renderer/gl/functions.h>
#include <renderer/gl/types.h>
#include <shader/spirv_recompiler.h>
#include <shader/usse_program_analyzer.h>
#include <shader/program_analyzer.h>
#include <shader/recompiler.h>
#include <display/state.h>
#include <features/state.h>
+5 -3
View File
@@ -27,7 +27,7 @@
#include <gxm/types.h>
#include <util/log.h>
#include <shader/spirv_recompiler.h>
#include <shader/recompiler.h>
#include <cmath>
@@ -212,7 +212,8 @@ void sync_clipping(GLContext &context) {
}
void sync_cull(const GxmRecordState &state) {
// Culling.
// Culling. We set front face to CCW by default. So we flip all faces here
// Because GL Y axis is negated.
switch (state.cull_mode) {
case SCE_GXM_CULL_CCW:
glEnable(GL_CULL_FACE);
@@ -251,7 +252,8 @@ void sync_depth_data(const renderer::GxmRecordState &state) {
}
void sync_stencil_func(const GxmStencilState &state, const MemState &mem, const bool is_back_stencil) {
const GLenum face = is_back_stencil ? GL_BACK : GL_FRONT;
// We flip faces in OpenGL in constrast to GXM's coordinate system
const GLenum face = is_back_stencil ? GL_FRONT : GL_BACK;
glStencilOpSeparate(face,
translate_stencil_op(state.stencil_fail),
+46 -22
View File
@@ -1,36 +1,60 @@
add_library(
shader
STATIC
include/shader/profile.h
include/shader/usse_types.h
include/shader/types_imm.h
include/shader/matcher.h
include/shader/glsl/code_writer.h
include/shader/glsl/params.h
include/shader/glsl/recompiler.h
include/shader/glsl/translator.h
include/shader/spirv/translator_entry.h
include/shader/spirv/translator.h
include/shader/spirv/utilities.h
include/shader/constant_table.h
include/shader/decoder_detail.h
include/shader/usse_disasm.h
include/shader/usse_program_analyzer.h
include/shader/usse_decoder_helpers.h
include/shader/usse_translator.h
include/shader/usse_translator_entry.h
include/shader/usse_translator_types.h
include/shader/usse_utilities.h
include/shader/decoder_helpers.h
include/shader/disasm.h
include/shader/gxp_parser.h
include/shader/spirv_recompiler.h
include/shader/matcher.h
include/shader/profile.h
include/shader/program_analyzer.h
include/shader/recompiler.h
include/shader/translator_types.h
include/shader/translator.h
include/shader/types_imm.h
include/shader/types.h
src/glsl/translator/alu.cpp
src/glsl/translator/ialu.cpp
src/glsl/translator/branch_cond.cpp
src/glsl/translator/data.cpp
src/glsl/translator/special.cpp
src/glsl/translator/texture.cpp
src/glsl/code_writer.cpp
src/glsl/params.cpp
src/glsl/recompiler.cpp
src/glsl/translator.cpp
src/spirv/translator/alu.cpp
src/spirv/translator/ialu.cpp
src/spirv/translator/branch_cond.cpp
src/spirv/translator/data.cpp
src/spirv/translator/special.cpp
src/spirv/translator/texture.cpp
src/spirv/translator/utils.cpp
src/spirv/recompiler.cpp
src/spirv/translator.cpp
src/spirv/utilities.cpp
src/translator/alu.cpp
src/translator/ialu.cpp
src/translator/branch_cond.cpp
src/translator/data.cpp
src/translator/special.cpp
src/translator/ialu.cpp
src/translator/illegal.cpp
src/translator/special.cpp
src/translator/texture.cpp
src/translator/utils.cpp
src/decode_helpers.cpp
src/disasm.cpp
src/gxp_parser.cpp
src/usse_disasm.cpp
src/usse_program_analyzer.cpp
src/usse_decode_helpers.cpp
src/usse_translator_entry.cpp
src/usse_utilities.cpp
src/spirv_recompiler.cpp
src/program_analyzer.cpp
src/translator.cpp
src/types.cpp
)
target_include_directories(shader PUBLIC include)
@@ -17,7 +17,7 @@
#pragma once
#include <shader/usse_types.h>
#include <shader/types.h>
namespace shader {
namespace usse {
@@ -17,7 +17,7 @@
#pragma once
#include <shader/usse_types.h>
#include <shader/types.h>
#include <fstream>
#include <memory>
@@ -0,0 +1,63 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <string>
struct FeatureState;
namespace shader::usse::glsl {
enum CodeBodyType {
BODY_TYPE_MAIN,
BODY_TYPE_SECONDARY_PROGRAM,
BODY_TYPE_PRIMARY_PROGRAM,
BODY_TYPE_POSTWORK, // Workaround for param loader
BODY_TYPE_MAX
};
class CodeWriter {
private:
std::string body_indentation[BODY_TYPE_MAX];
std::string preload_indentation;
std::string postwork_indentation;
std::string decl_indentation;
std::string decl_content;
std::string preload_content;
std::string body_content[BODY_TYPE_MAX];
std::string post_content;
CodeBodyType active_body_type;
public:
explicit CodeWriter();
void add_declaration(const std::string &decl_line);
void add_to_preload(const std::string &line);
void add_to_current_body(const std::string &line);
void indent_preload();
void dedent_preload();
void indent_current_body();
void dedent_current_body();
void indent_declaration();
void dedent_declaration();
void set_active_body_type(const CodeBodyType type);
std::string assemble();
};
} // namespace shader::usse::glsl
@@ -0,0 +1,24 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
namespace shader::usse::glsl {
static const char *VERTEX_UB_GROUP_NAME = "vertexData";
static const char *FRAGMENT_UB_GROUP_NAME = "fragmentData";
static const char *UB_MEMBER_NAME_FORMAT = "buffer{}";
} // namespace shader::usse::glsl
+135
View File
@@ -0,0 +1,135 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <map>
#include <queue>
#include <shader/types.h>
#include <stack>
namespace shader::usse::glsl {
class CodeWriter;
struct ShaderVariableVec4Info {
// Each int represents the previous type that has not been flushed. If the new dest type is different, flush
// and reassign!
int dirty[4];
bool inited = false;
enum DirtyType {
DIRTY_TYPE_U8 = 0,
DIRTY_TYPE_F16 = 1,
DIRTY_TYPE_F32 = 2,
DIRTY_TYPE_S8 = 3,
DIRTY_TYPE_U16 = 4,
DIRTY_TYPE_S16 = 5,
DIRTY_TYPE_MAX = 6
};
enum DeclaredType {
DECLARED_FLOAT4 = 1 << 0,
DECLARED_HALF4_N1 = 1 << 1,
DECLARED_HALF4_N2 = 1 << 2,
DECLARED_U8_N1 = 1 << 3,
DECLARED_U8_N2 = 1 << 4,
DECLARED_U8_N3 = 1 << 5,
DECLARED_U8_N4 = 1 << 6,
DECLARED_S8_N1 = 1 << 7,
DECLARED_S8_N2 = 1 << 8,
DECLARED_S8_N3 = 1 << 9,
DECLARED_S8_N4 = 1 << 10,
DECLARED_U8_SHIFT_START = 3,
DECLARED_S8_SHIFT_START = 7,
DECLARED_U16_N1 = 1 << 11,
DECLARED_U16_N2 = 1 << 12,
DECLARED_S16_N1 = 1 << 13,
DECLARED_S16_N2 = 1 << 14
};
std::uint32_t declared = 0;
explicit ShaderVariableVec4Info();
};
struct RestorationInfo {
ShaderVariableVec4Info &info;
int component;
int original_type;
int base_current;
std::uint32_t decl;
};
struct ShaderVariableBank {
std::map<int, ShaderVariableVec4Info> variables;
std::string prefix;
explicit ShaderVariableBank(const std::string &prefix)
: prefix(prefix) {
}
};
struct ShaderVariables {
private:
ShaderVariableBank pa_bank;
ShaderVariableBank sa_bank;
ShaderVariableBank internal_bank;
ShaderVariableBank temp_bank;
ShaderVariableBank output_bank;
bool index_variables[2];
bool should_gen_indexing_lookup[4]; // TEMP, OUTPUT, PA, SA
bool should_gen_vecint_bitcast[4];
bool should_gen_vecfloat_bitcast[6];
bool should_gen_clamp16;
CodeWriter &writer;
const ProgramInput &program_input;
bool is_vertex;
ShaderVariableBank &get_load_bank(const RegisterBank bank);
void do_restore(std::queue<RestorationInfo> &infos, const std::string &prefix);
void apply_modifiers(const std::uint32_t flags, std::string &result);
void prepare_variables(ShaderVariableBank &bank, const DataType dt, const RegisterBank bank_type, const int index, const int shift, const Swizzle4 &swizz,
const std::uint8_t dest_mask, const bool for_write, std::string &output_prefix, int &real_load_index, bool &load_crossing_to_another_vector);
void flush_bank(ShaderVariableBank &bank);
public:
enum GenPackUnpackKind {
GEN_PACK_U8,
GEN_PACK_S8,
GEN_UNPACK_U8,
GEN_UNPACK_S8,
GEN_PACK_F16,
GEN_UNPACK_F16,
GEN_PACK_U16,
GEN_PACK_S16,
GEN_UNPACK_S16,
GEN_UNPACK_U16,
GEN_MAX
};
bool should_gen_pack_unpack[GEN_MAX];
explicit ShaderVariables(CodeWriter &writer, const ProgramInput &input, const bool is_vertex);
std::string load(const Operand &op, const std::uint8_t dest_mask, const int shift_offset);
void store(const Operand &dest, const std::string &rhs, const std::uint8_t dest_mask, const int shift_offset, const bool prepare_store = false);
void generate_helper_functions();
void flush();
void mark_f32_raw_dirty(const RegisterBank bank, const int offset);
};
} // namespace shader::usse::glsl
@@ -0,0 +1,62 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <map>
#include <vector>
struct SceGxmProgram;
namespace shader::usse::glsl {
using SamplerMap = std::map<int, std::pair<std::string, bool>>;
struct VarToReg {
std::string var_name;
int offset;
int location;
int comp_count;
int data_type;
bool reg_format;
bool builtin;
};
struct NonDependentTextureQueryInfo {
int sampler_index;
int coord_index;
int proj_pos;
std::string sampler_name;
bool sampler_cube;
int data_type;
int offset_in_pa;
};
using NonDependentTextureQueries = std::vector<NonDependentTextureQueryInfo>;
struct ProgramState {
SamplerMap samplers_on_offset;
NonDependentTextureQueries non_dependent_queries;
const SceGxmProgram &actual_program;
bool should_generate_vld_func;
explicit ProgramState(const SceGxmProgram &actual_program)
: actual_program(actual_program)
, should_generate_vld_func(false) {
}
};
} // namespace shader::usse::glsl
@@ -0,0 +1,600 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <gxm/types.h>
#include <shader/glsl/recompiler.h>
#include <shader/program_analyzer.h>
#include <shader/translator.h>
#include <shader/translator_types.h>
#include <array>
#include <map>
struct FeatureState;
namespace shader::usse {
struct USSERecompiler;
}
namespace shader::usse::glsl {
class CodeWriter;
struct ShaderVariables;
struct ProgramState;
class USSETranslatorVisitorGLSL : public USSETranslatorVisitor {
public:
void do_texture_queries(const NonDependentTextureQueries &texture_queries);
USSETranslatorVisitorGLSL() = delete;
explicit USSETranslatorVisitorGLSL(USSERecompiler &_recompiler, ProgramState &program, CodeWriter &writer,
ShaderVariables &variables, const FeatureState &features, const uint64_t &_instr,
bool is_secondary_program = false)
: USSETranslatorVisitor(_recompiler, program.actual_program, features, _instr, is_secondary_program)
, writer(writer)
, variables(variables)
, program_state(program) {
do_texture_queries(program.non_dependent_queries);
}
public:
// Instructions start
bool vmad2(Imm1 dat_fmt,
Imm2 pred,
Imm1 skipinv,
Imm1 src0_swiz_bits2,
Imm1 syncstart,
Imm1 src0_abs,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 src2_swiz,
Imm1 src1_swiz_bit2,
Imm1 nosched,
Imm4 dest_mask,
Imm2 src1_mod,
Imm2 src2_mod,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm6 dest_n,
Imm2 src1_swiz_bits01,
Imm2 src0_swiz_bits01,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) override;
bool v32nmad(ExtVecPredicate pred,
bool skipinv,
Imm2 src1_swiz_10_11,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 src1_swiz_9,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 src2_swiz,
bool nosched,
Imm4 dest_mask,
Imm2 src1_mod,
Imm1 src2_mod,
Imm2 src1_swiz_7_8,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm6 dest_n,
Imm7 src1_swiz_0_6,
Imm3 op2,
Imm6 src1_n,
Imm6 src2_n) override;
bool vmad(ExtVecPredicate pred,
Imm1 skipinv,
Imm1 gpi1_swiz_ext,
Imm1 opcode2,
Imm1 dest_use_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
RepeatMode repeat_mode,
Imm1 gpi0_abs,
RepeatCount repeat_count,
bool nosched,
Imm4 write_mask,
Imm1 src1_neg,
Imm1 src1_abs,
Imm1 gpi1_neg,
Imm1 gpi1_abs,
Imm1 gpi0_swiz_ext,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 gpi0_n,
Imm6 dest_n,
Imm4 gpi0_swiz,
Imm4 gpi1_swiz,
Imm2 gpi1_n,
Imm1 gpi0_neg,
Imm1 src1_swiz_ext,
Imm4 src1_swiz,
Imm6 src1_n) override;
bool vdp(ExtVecPredicate pred,
Imm1 skipinv,
bool clip_plane_enable,
Imm1 opcode2,
Imm1 dest_use_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
RepeatMode repeat_mode,
Imm1 gpi0_abs,
RepeatCount repeat_count,
bool nosched,
Imm4 write_mask,
Imm1 src1_neg,
Imm1 src1_abs,
Imm3 clip_plane_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 gpi0_n,
Imm6 dest_n,
Imm4 gpi0_swiz,
Imm3 src1_swiz_w,
Imm3 src1_swiz_z,
Imm3 src1_swiz_y,
Imm3 src1_swiz_x,
Imm6 src1_n) override;
bool vdual(Imm1 comp_count_type,
Imm1 gpi1_neg,
Imm2 sv_pred,
Imm1 skipinv,
Imm1 dual_op1_ext_vec3_or_has_w_vec4,
bool type_f16,
Imm1 gpi1_swizz_ext,
Imm4 unified_store_swizz,
Imm1 unified_store_neg,
Imm3 dual_op1,
Imm1 dual_op2_ext,
bool prim_ustore,
Imm4 gpi0_swizz,
Imm4 gpi1_swizz,
Imm2 prim_dest_bank,
Imm2 unified_store_slot_bank,
Imm2 prim_dest_num_gpi_case,
Imm7 prim_dest_num,
Imm3 dual_op2,
Imm2 src_config,
Imm1 gpi2_slot_num_bit_1,
Imm1 gpi2_slot_num_bit_0_or_unified_store_abs,
Imm2 gpi1_slot_num,
Imm2 gpi0_slot_num,
Imm3 write_mask_non_gpi,
Imm7 unified_store_slot_num) override;
bool vcomp(ExtPredicate pred,
bool skipinv,
Imm2 dest_type,
bool syncstart,
bool dest_bank_ext,
bool end,
bool src1_bank_ext,
RepeatCount repeat_count,
bool nosched,
Imm2 op2,
Imm2 src_type,
Imm2 src1_mod,
Imm2 src_comp,
Imm2 dest_bank,
Imm2 src1_bank,
Imm7 dest_n,
Imm7 src1_n,
Imm4 write_mask) override;
bool vmov(ExtPredicate pred,
bool skipinv,
Imm1 test_bit_2,
Imm1 src0_comp_sel,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end_or_src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
MoveType move_type,
RepeatCount repeat_count,
bool nosched,
DataType move_data_type,
Imm1 test_bit_1,
Imm4 src0_swiz,
Imm1 src0_bank_sel,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm4 dest_mask,
Imm6 dest_n,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) override;
bool vpck(ExtPredicate pred,
bool skipinv,
bool nosched,
Imm1 unknown,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
RepeatCount repeat_count,
Imm3 src_fmt,
Imm3 dest_fmt,
Imm4 dest_mask,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm7 dest_n,
Imm2 comp_sel_3,
Imm1 scale,
Imm2 comp_sel_1,
Imm2 comp_sel_2,
Imm6 src1_n,
Imm1 comp0_sel_bit1,
Imm6 src2_n,
Imm1 comp_sel_0_bit0) override;
bool vtst(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 src1_neg,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm3 chan_cc,
Imm2 pdst_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) override;
bool vtstmsk(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 test_flag_2,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm2 tst_mask_type,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) override;
bool vbw(Imm3 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
bool repeat_sel,
Imm1 sync_start,
Imm1 dest_ext,
Imm1 end,
Imm1 src1_ext,
Imm1 src2_ext,
RepeatCount repeat_count,
Imm1 src2_invert,
Imm5 src2_rot,
Imm2 src2_exth,
Imm1 op2,
Imm1 bitwise_partial,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src2_sel,
Imm7 src1_n,
Imm7 src2_n) override;
bool sop2(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 asel1,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 count,
Imm1 amod1,
Imm2 asel2,
Imm3 csel1,
Imm3 csel2,
Imm1 amod2,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 src1_mod,
Imm2 cop,
Imm2 aop,
Imm1 asrc1_mod,
Imm1 dest_mod,
Imm7 src1_n,
Imm7 src2_n) override;
bool sop2m(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 cop,
Imm1 destbankext,
Imm1 end,
Imm1 src1bankext,
Imm1 src2bankext,
Imm1 mod2,
Imm4 wmask,
Imm2 aop,
Imm3 sel1,
Imm3 sel2,
Imm2 destbank,
Imm2 src1bank,
Imm2 src2bank,
Imm7 destnum,
Imm7 src1num,
Imm7 src2num) override;
bool sop3(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 cop,
Imm1 destbext,
Imm1 end,
Imm1 src1bext,
Imm1 src2bext,
Imm1 mod2,
Imm4 wrmask,
Imm2 aop,
Imm3 sel1,
Imm3 sel2,
Imm1 src0bank,
Imm2 destbank,
Imm2 src1bank,
Imm2 src2bank,
Imm7 destn,
Imm7 src0n,
Imm7 src1n,
Imm7 src2n) override;
bool i8mad(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 csel0,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 repeat_count,
Imm1 saturated,
Imm1 cmod0,
Imm1 asel0,
Imm1 amod2,
Imm1 amod1,
Imm1 amod0,
Imm1 csel1,
Imm1 csel2,
Imm1 src0_neg,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_num,
Imm7 src0_num,
Imm7 src1_num,
Imm7 src2_num) override;
bool i16mad(ShortPredicate pred,
Imm1 abs,
Imm1 skipinv,
Imm1 nosched,
Imm1 src2_neg,
Imm1 sel1h_upper8,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 repeat_count,
Imm2 mode,
Imm2 src2_format,
Imm2 src1_format,
Imm1 sel2h_upper8,
Imm2 or_shift,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool i32mad(ShortPredicate pred,
Imm1 src0_high,
Imm1 nosched,
Imm1 src1_high,
Imm1 src2_high,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
RepeatCount repeat_count,
bool is_signed,
bool is_sat,
Imm2 src2_type,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool i32mad2(ExtPredicate pred,
Imm1 nosched,
Imm2 sn,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
bool src0_bank_ext,
Imm3 count,
bool is_signed,
Imm1 negative_src1,
Imm1 negative_src2,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool smp(ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 syncstart,
Imm1 minpack,
Imm1 src0_ext,
Imm1 src1_ext,
Imm1 src2_ext,
Imm2 fconv_type,
Imm2 mask_count,
Imm2 dim,
Imm2 lod_mode,
bool dest_use_pa,
Imm2 sb_mode,
Imm2 src0_type,
Imm1 src0_bank,
Imm2 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool kill(ShortPredicate pred) override;
bool limm(bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
Imm6 imm_value_bits26to31,
ExtPredicate pred,
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits) override;
bool vldst(Imm2 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 moe_expand,
Imm1 sync_start,
Imm1 cache_ext,
Imm1 src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 mask_count,
Imm2 addr_mode,
Imm2 mode,
Imm1 dest_bank_primattr,
Imm1 range_enable,
Imm2 data_type,
Imm1 increment_or_decrement,
Imm1 src0_bank,
Imm1 cache_by_pass12,
Imm1 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
// Instructions end
std::string do_alu_op(Instruction &inst, const Imm4 source_mask, const Imm4 possible_dest_mask);
private:
CodeWriter &writer;
ShaderVariables &variables;
ProgramState &program_state;
std::string vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask);
std::string do_fetch_texture(const std::string tex, std::string coord_name, const DataType dest_type, const int lod_mode,
const std::string extra1, const std::string extra2);
};
struct USSERecompilerGLSL : public USSERecompiler {
std::uint32_t cond_stack = 0;
bool cond_decled[3]{ false, false, false };
CodeWriter &writer;
ShaderVariables &variables;
explicit USSERecompilerGLSL(ProgramState &program_state, CodeWriter &writer, ShaderVariables &variables, const FeatureState &features);
void compile_break_node(const usse::USSEBreakNode &node) override;
void compile_continue_node(const usse::USSEContinueNode &node) override;
void compile_conditional_node(const usse::USSEConditionalNode &cond) override;
void compile_loop_node(const usse::USSELoopNode &loop) override;
void compile_code_node(const usse::USSECodeNode &code) override;
std::string get_condition(std::uint8_t num, bool do_neg);
void compile_to_function();
void begin_condition(const int cond) override;
void end_condition() override;
};
} // namespace shader::usse::glsl
+3 -2
View File
@@ -17,9 +17,10 @@
#pragma once
#include <gxm/functions.h>
#include <gxm/types.h>
#include <shader/usse_translator_types.h>
#include <shader/usse_types.h>
#include <shader/translator_types.h>
#include <shader/types.h>
namespace shader {
@@ -23,7 +23,7 @@
#include <map>
#include <memory>
#include <queue>
#include <shader/usse_types.h>
#include <shader/types.h>
#include <tuple>
#include <vector>
@@ -18,8 +18,8 @@
#pragma once
#include <gxm/types.h>
#include <shader/usse_translator_types.h>
#include <shader/usse_types.h>
#include <shader/translator_types.h>
#include <shader/types.h>
#include <features/state.h>
@@ -27,10 +27,6 @@
#include <utility>
#include <vector>
namespace spv {
class Builder;
}
namespace shader {
static constexpr int COLOR_ATTACHMENT_TEXTURE_SLOT_IMAGE = 0;
static constexpr int MASK_TEXTURE_SLOT_IMAGE = 1;
@@ -41,14 +37,12 @@ static constexpr float INTEGRAL_TEX_QUERY_TYPE_16BIT = 1.0;
static constexpr float INTEGRAL_TEX_QUERY_TYPE_32BIT = 0.0;
static constexpr std::uint32_t CURRENT_VERSION = 2;
// Dump generated SPIR-V disassembly up to this point
void spirv_disasm_print(const usse::SpirvCode &spirv_binary, std::string *spirv_dump = nullptr);
usse::SpirvCode convert_gxp_to_spirv(const SceGxmProgram &program, const std::string &shader_hash, const FeatureState &features, const std::vector<SceGxmVertexAttribute> *hint_attributes = nullptr, bool maskupdate = false,
bool force_shader_debug = false, std::function<bool(const std::string &ext, const std::string &dump)> dumper = nullptr);
std::string convert_gxp_to_glsl(const SceGxmProgram &program, const std::string &shader_hash, const FeatureState &features,
const std::vector<SceGxmVertexAttribute> *hint_attributes = nullptr, bool maskupdate = false, bool force_shader_debug = false, std::function<bool(const std::string &ext, const std::string &dump)> dumper = nullptr);
void convert_gxp_to_glsl_from_filepath(const std::string &shader_filepath);
} // namespace shader
@@ -0,0 +1,662 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <gxm/types.h>
#include <shader/program_analyzer.h>
#include <shader/spirv/utilities.h>
#include <shader/translator.h>
#include <shader/translator_types.h>
#include <SPIRV/SpvBuilder.h>
#include <array>
#include <map>
struct FeatureState;
namespace shader::usse {
// For debugging SPIR-V output
static uint32_t instr_idx = 0;
constexpr std::size_t max_sa_registers = 128;
struct USSERecompiler;
class USSETranslatorVisitorSpirv : public USSETranslatorVisitor {
public:
spv::Id std_builtins;
spv::Id type_f32;
spv::Id type_ui32;
spv::Id type_f32_v[5]; // Starts from 1 ([1] is vec 1)
spv::Id const_f32[4];
spv::Id const_f32_v0[5];
utils::SpirvUtilFunctions m_util_funcs;
spv::Block *main_block;
spv::Id out;
void do_texture_queries(const NonDependentTextureQueryCallInfos &texture_queries, const spv::Id translation_state_id);
spv::Id do_fetch_texture(const spv::Id tex, const Coord &coord, const DataType dest_type, const int lod_mode,
const spv::Id lod = spv::NoResult);
USSETranslatorVisitorSpirv() = delete;
explicit USSETranslatorVisitorSpirv(spv::Builder &_b, USSERecompiler &_recompiler, const SceGxmProgram &program, const FeatureState &features,
utils::SpirvUtilFunctions &utils, const uint64_t &_instr, const SpirvShaderParameters &spirv_params, const NonDependentTextureQueryCallInfos &queries,
const spv::Id render_info_id, bool is_secondary_program = false)
: USSETranslatorVisitor(_recompiler, program, features, _instr, is_secondary_program)
, m_util_funcs(utils)
, m_b(_b)
, m_spirv_params(spirv_params) {
out = spv::NoResult;
// Set main block
main_block = m_b.getBuildPoint();
// Import GLSL.std.450
std_builtins = m_b.import("GLSL.std.450");
// Build common type here, so builder won't have to look it up later
type_f32 = m_b.makeFloatType(32);
type_ui32 = m_b.makeUintType(32);
const_f32[3] = m_b.makeFloatConstant(0.5f);
const_f32[0] = m_b.makeFloatConstant(0.0f);
const_f32[1] = m_b.makeFloatConstant(1.0f);
const_f32[2] = m_b.makeFloatConstant(2.0f);
for (std::uint8_t i = 1; i < 5; i++) {
if (i == 1) {
type_f32_v[i] = type_f32;
const_f32_v0[i] = const_f32[0];
} else {
type_f32_v[i] = m_b.makeVectorType(type_f32, i);
std::vector<spv::Id> consts;
for (std::uint8_t j = 1; j < i + 1; j++) {
consts.push_back(const_f32[0]);
}
const_f32_v0[i] = m_b.makeCompositeConstant(type_f32_v[i], consts);
}
}
do_texture_queries(queries, render_info_id);
}
/*
* \brief Given an operand, load it and returns a SPIR-V vector with total components count equals to total bit set in
* write/dest mask
*
* \returns A copy of given operand
*/
spv::Id load(Operand op, const Imm4 dest_mask, int shift_offset = 0);
private:
void store(Operand dest, spv::Id source, std::uint8_t dest_mask = 0xFF, int shift_offset = 0);
spv::Id swizzle_to_spv_comp(spv::Id composite, spv::Id type, SwizzleChannel swizzle);
spv::Id do_alu_op(Instruction &inst, const Imm4 source_mask, const Imm4 possible_dest_mask);
public:
// Instructions start
bool vmad2(Imm1 dat_fmt,
Imm2 pred,
Imm1 skipinv,
Imm1 src0_swiz_bits2,
Imm1 syncstart,
Imm1 src0_abs,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 src2_swiz,
Imm1 src1_swiz_bit2,
Imm1 nosched,
Imm4 dest_mask,
Imm2 src1_mod,
Imm2 src2_mod,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm6 dest_n,
Imm2 src1_swiz_bits01,
Imm2 src0_swiz_bits01,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) override;
bool v32nmad(ExtVecPredicate pred,
bool skipinv,
Imm2 src1_swiz_10_11,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 src1_swiz_9,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 src2_swiz,
bool nosched,
Imm4 dest_mask,
Imm2 src1_mod,
Imm1 src2_mod,
Imm2 src1_swiz_7_8,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm6 dest_n,
Imm7 src1_swiz_0_6,
Imm3 op2,
Imm6 src1_n,
Imm6 src2_n) override;
bool vmad(ExtVecPredicate pred,
Imm1 skipinv,
Imm1 gpi1_swiz_ext,
Imm1 opcode2,
Imm1 dest_use_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
RepeatMode repeat_mode,
Imm1 gpi0_abs,
RepeatCount repeat_count,
bool nosched,
Imm4 write_mask,
Imm1 src1_neg,
Imm1 src1_abs,
Imm1 gpi1_neg,
Imm1 gpi1_abs,
Imm1 gpi0_swiz_ext,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 gpi0_n,
Imm6 dest_n,
Imm4 gpi0_swiz,
Imm4 gpi1_swiz,
Imm2 gpi1_n,
Imm1 gpi0_neg,
Imm1 src1_swiz_ext,
Imm4 src1_swiz,
Imm6 src1_n) override;
bool vdp(ExtVecPredicate pred,
Imm1 skipinv,
bool clip_plane_enable,
Imm1 opcode2,
Imm1 dest_use_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
RepeatMode repeat_mode,
Imm1 gpi0_abs,
RepeatCount repeat_count,
bool nosched,
Imm4 write_mask,
Imm1 src1_neg,
Imm1 src1_abs,
Imm3 clip_plane_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 gpi0_n,
Imm6 dest_n,
Imm4 gpi0_swiz,
Imm3 src1_swiz_w,
Imm3 src1_swiz_z,
Imm3 src1_swiz_y,
Imm3 src1_swiz_x,
Imm6 src1_n) override;
bool vdual(Imm1 comp_count_type,
Imm1 gpi1_neg,
Imm2 sv_pred,
Imm1 skipinv,
Imm1 dual_op1_ext_vec3_or_has_w_vec4,
bool type_f16,
Imm1 gpi1_swizz_ext,
Imm4 unified_store_swizz,
Imm1 unified_store_neg,
Imm3 dual_op1,
Imm1 dual_op2_ext,
bool prim_ustore,
Imm4 gpi0_swizz,
Imm4 gpi1_swizz,
Imm2 prim_dest_bank,
Imm2 unified_store_slot_bank,
Imm2 prim_dest_num_gpi_case,
Imm7 prim_dest_num,
Imm3 dual_op2,
Imm2 src_config,
Imm1 gpi2_slot_num_bit_1,
Imm1 gpi2_slot_num_bit_0_or_unified_store_abs,
Imm2 gpi1_slot_num,
Imm2 gpi0_slot_num,
Imm3 write_mask_non_gpi,
Imm7 unified_store_slot_num) override;
bool vcomp(ExtPredicate pred,
bool skipinv,
Imm2 dest_type,
bool syncstart,
bool dest_bank_ext,
bool end,
bool src1_bank_ext,
RepeatCount repeat_count,
bool nosched,
Imm2 op2,
Imm2 src_type,
Imm2 src1_mod,
Imm2 src_comp,
Imm2 dest_bank,
Imm2 src1_bank,
Imm7 dest_n,
Imm7 src1_n,
Imm4 write_mask) override;
bool vmov(ExtPredicate pred,
bool skipinv,
Imm1 test_bit_2,
Imm1 src0_comp_sel,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end_or_src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
MoveType move_type,
RepeatCount repeat_count,
bool nosched,
DataType move_data_type,
Imm1 test_bit_1,
Imm4 src0_swiz,
Imm1 src0_bank_sel,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm4 dest_mask,
Imm6 dest_n,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) override;
bool vpck(ExtPredicate pred,
bool skipinv,
bool nosched,
Imm1 unknown,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
RepeatCount repeat_count,
Imm3 src_fmt,
Imm3 dest_fmt,
Imm4 dest_mask,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm7 dest_n,
Imm2 comp_sel_3,
Imm1 scale,
Imm2 comp_sel_1,
Imm2 comp_sel_2,
Imm6 src1_n,
Imm1 comp0_sel_bit1,
Imm6 src2_n,
Imm1 comp_sel_0_bit0) override;
bool vtst(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 src1_neg,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm3 chan_cc,
Imm2 pdst_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) override;
bool vtstmsk(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 test_flag_2,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm2 tst_mask_type,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) override;
bool vbw(Imm3 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
bool repeat_sel,
Imm1 sync_start,
Imm1 dest_ext,
Imm1 end,
Imm1 src1_ext,
Imm1 src2_ext,
RepeatCount repeat_count,
Imm1 src2_invert,
Imm5 src2_rot,
Imm2 src2_exth,
Imm1 op2,
Imm1 bitwise_partial,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src2_sel,
Imm7 src1_n,
Imm7 src2_n) override;
bool sop2(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 asel1,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 count,
Imm1 amod1,
Imm2 asel2,
Imm3 csel1,
Imm3 csel2,
Imm1 amod2,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 src1_mod,
Imm2 cop,
Imm2 aop,
Imm1 asrc1_mod,
Imm1 dest_mod,
Imm7 src1_n,
Imm7 src2_n) override;
bool sop2m(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 cop,
Imm1 destbankext,
Imm1 end,
Imm1 src1bankext,
Imm1 src2bankext,
Imm1 mod2,
Imm4 wmask,
Imm2 aop,
Imm3 sel1,
Imm3 sel2,
Imm2 destbank,
Imm2 src1bank,
Imm2 src2bank,
Imm7 destnum,
Imm7 src1num,
Imm7 src2num) override;
bool sop3(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 cop,
Imm1 destbext,
Imm1 end,
Imm1 src1bext,
Imm1 src2bext,
Imm1 mod2,
Imm4 wrmask,
Imm2 aop,
Imm3 sel1,
Imm3 sel2,
Imm1 src0bank,
Imm2 destbank,
Imm2 src1bank,
Imm2 src2bank,
Imm7 destn,
Imm7 src0n,
Imm7 src1n,
Imm7 src2n) override;
bool i8mad(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 csel0,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 repeat_count,
Imm1 saturated,
Imm1 cmod0,
Imm1 asel0,
Imm1 amod2,
Imm1 amod1,
Imm1 amod0,
Imm1 csel1,
Imm1 csel2,
Imm1 src0_neg,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_num,
Imm7 src0_num,
Imm7 src1_num,
Imm7 src2_num) override;
bool i16mad(ShortPredicate pred,
Imm1 abs,
Imm1 skipinv,
Imm1 nosched,
Imm1 src2_neg,
Imm1 sel1h_upper8,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 repeat_count,
Imm2 mode,
Imm2 src2_format,
Imm2 src1_format,
Imm1 sel2h_upper8,
Imm2 or_shift,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool i32mad(ShortPredicate pred,
Imm1 src0_high,
Imm1 nosched,
Imm1 src1_high,
Imm1 src2_high,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
RepeatCount repeat_count,
bool is_signed,
bool is_sat,
Imm2 src2_type,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool i32mad2(ExtPredicate pred,
Imm1 nosched,
Imm2 sn,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
bool src0_bank_ext,
Imm3 count,
bool is_signed,
Imm1 negative_src1,
Imm1 negative_src2,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool smp(ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 syncstart,
Imm1 minpack,
Imm1 src0_ext,
Imm1 src1_ext,
Imm1 src2_ext,
Imm2 fconv_type,
Imm2 mask_count,
Imm2 dim,
Imm2 lod_mode,
bool dest_use_pa,
Imm2 sb_mode,
Imm2 src0_type,
Imm1 src0_bank,
Imm2 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
bool kill(ShortPredicate pred) override;
bool limm(bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
Imm6 imm_value_bits26to31,
ExtPredicate pred,
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits) override;
bool vldst(Imm2 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 moe_expand,
Imm1 sync_start,
Imm1 cache_ext,
Imm1 src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 mask_count,
Imm2 addr_mode,
Imm2 mode,
Imm1 dest_bank_primattr,
Imm1 range_enable,
Imm2 data_type,
Imm1 increment_or_decrement,
Imm1 src0_bank,
Imm1 cache_by_pass12,
Imm1 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) override;
// Instructions end
private:
spv::Id vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask);
// SPIR-V emitter
spv::Builder &m_b;
// SPIR-V IDs
const SpirvShaderParameters &m_spirv_params;
};
struct USSERecompilerSpirv : public USSERecompiler {
std::stack<spv::Builder::If> cond_stacks;
spv::Builder &b;
USSETranslatorVisitorSpirv *get_spirv_translator_visitor();
explicit USSERecompilerSpirv(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features,
const SpirvShaderParameters &parameters, utils::SpirvUtilFunctions &utils, const NonDependentTextureQueryCallInfos &queries,
const spv::Id render_info_id);
void compile_break_node(const usse::USSEBreakNode &node) override;
void compile_continue_node(const usse::USSEContinueNode &node) override;
void compile_conditional_node(const usse::USSEConditionalNode &cond) override;
void compile_loop_node(const usse::USSELoopNode &loop) override;
void begin_condition(const int cond) override;
void end_condition() override;
spv::Id get_condition_value(const std::uint8_t pred, const bool neg = false);
spv::Function *compile_program_function();
};
} // namespace shader::usse
@@ -18,9 +18,9 @@
#pragma once
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_program_analyzer.h>
#include <shader/usse_translator_types.h>
#include <shader/usse_types.h>
#include <shader/program_analyzer.h>
#include <shader/translator_types.h>
#include <shader/types.h>
#include <gxm/types.h>
@@ -55,8 +55,6 @@ spv::Id unwrap_type(spv::Builder &b, spv::Id type);
spv::Id convert_to_float(spv::Builder &b, spv::Id opr, DataType type, bool normal);
spv::Id convert_to_int(spv::Builder &b, spv::Id opr, DataType type, bool normal);
size_t dest_mask_to_comp_count(shader::usse::Imm4 dest_mask);
template <typename T>
spv::Id make_uniform_vector_from_type(spv::Builder &b, spv::Id type, T val) {
const int num_comp = b.getNumTypeComponents(type);
@@ -18,12 +18,9 @@
#pragma once
#include <gxm/types.h>
#include <shader/spirv_recompiler.h>
#include <shader/usse_program_analyzer.h>
#include <shader/usse_translator_types.h>
#include <shader/usse_utilities.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/program_analyzer.h>
#include <shader/recompiler.h>
#include <shader/translator_types.h>
#include <array>
#include <map>
@@ -32,103 +29,32 @@ struct FeatureState;
namespace shader::usse {
// For debugging SPIR-V output
static uint32_t instr_idx = 0;
constexpr std::size_t max_sa_registers = 128;
struct USSERecompiler;
class USSETranslatorVisitor final {
class USSETranslatorVisitor {
public:
using instruction_return_type = bool;
spv::Id std_builtins;
spv::Id type_f32;
spv::Id type_ui32;
spv::Id type_f32_v[5]; // Starts from 1 ([1] is vec 1)
spv::Id const_f32[4];
spv::Id const_f32_v0[5];
utils::SpirvUtilFunctions m_util_funcs;
spv::Block *main_block;
spv::Id out;
// Contains repeat increasement offset
int repeat_increase[4][4];
int repeat_multiplier[4];
void do_texture_queries(const NonDependentTextureQueryCallInfos &texture_queries, const spv::Id translation_state_id);
spv::Id do_fetch_texture(const spv::Id tex, const Coord &coord, const DataType dest_type, const int lod_mode,
const spv::Id lod = spv::NoResult);
USSETranslatorVisitor() = delete;
explicit USSETranslatorVisitor(spv::Builder &_b, USSERecompiler &_recompiler, const SceGxmProgram &program, const FeatureState &features,
utils::SpirvUtilFunctions &utils, const uint64_t &_instr, const SpirvShaderParameters &spirv_params, const NonDependentTextureQueryCallInfos &queries,
const spv::Id render_info_id, bool is_secondary_program = false)
: m_util_funcs(utils)
, m_second_program(is_secondary_program)
, m_b(_b)
explicit USSETranslatorVisitor(USSERecompiler &_recompiler, const SceGxmProgram &program, const FeatureState &features, const uint64_t &_instr, bool is_secondary_program = false)
: m_second_program(is_secondary_program)
, m_instr(_instr)
, m_spirv_params(spirv_params)
, m_recompiler(_recompiler)
, m_program(program)
, m_features(features) {
reset_for_new_session();
out = spv::NoResult;
// Set main block
main_block = m_b.getBuildPoint();
// Import GLSL.std.450
std_builtins = m_b.import("GLSL.std.450");
// Build common type here, so builder won't have to look it up later
type_f32 = m_b.makeFloatType(32);
type_ui32 = m_b.makeUintType(32);
const_f32[3] = m_b.makeFloatConstant(0.5f);
const_f32[0] = m_b.makeFloatConstant(0.0f);
const_f32[1] = m_b.makeFloatConstant(1.0f);
const_f32[2] = m_b.makeFloatConstant(2.0f);
for (std::uint8_t i = 1; i < 5; i++) {
if (i == 1) {
type_f32_v[i] = type_f32;
const_f32_v0[i] = const_f32[0];
} else {
type_f32_v[i] = m_b.makeVectorType(type_f32, i);
std::vector<spv::Id> consts;
for (std::uint8_t j = 1; j < i + 1; j++) {
consts.push_back(const_f32[0]);
}
const_f32_v0[i] = m_b.makeCompositeConstant(type_f32_v[i], consts);
}
}
do_texture_queries(queries, render_info_id);
}
/*
* \brief Given an operand, load it and returns a SPIR-V vector with total components count equals to total bit set in
* write/dest mask
*
* \returns A copy of given operand
*/
spv::Id load(Operand op, const Imm4 dest_mask, int shift_offset = 0);
void reset_for_new_session() {
reset_repeat_multiplier();
reset_repeat_increase();
}
private:
protected:
//
// Translation helpers
//
@@ -195,16 +121,9 @@ private:
}
}
void store(Operand dest, spv::Id source, std::uint8_t dest_mask = 0xFF, int shift_offset = 0);
spv::Id swizzle_to_spv_comp(spv::Id composite, spv::Id type, SwizzleChannel swizzle);
// TODO: Separate file for translator helpers?
static size_t dest_mask_to_comp_count(Imm4 dest_mask);
bool m_second_program{ false };
spv::Id do_alu_op(Instruction &inst, const Imm4 source_mask, const Imm4 possible_dest_mask);
public:
void set_secondary_program(const bool is_it) {
m_second_program = is_it;
@@ -215,7 +134,7 @@ public:
}
// Instructions start
bool vmad2(Imm1 dat_fmt,
virtual bool vmad2(Imm1 dat_fmt,
Imm2 pred,
Imm1 skipinv,
Imm1 src0_swiz_bits2,
@@ -240,7 +159,7 @@ public:
Imm6 src1_n,
Imm6 src2_n);
bool v32nmad(ExtVecPredicate pred,
virtual bool v32nmad(ExtVecPredicate pred,
bool skipinv,
Imm2 src1_swiz_10_11,
bool syncstart,
@@ -263,7 +182,7 @@ public:
Imm6 src1_n,
Imm6 src2_n);
bool v16nmad(ExtVecPredicate pred,
virtual bool v16nmad(ExtVecPredicate pred,
bool skipinv,
Imm2 src1_swiz_10_11,
bool syncstart,
@@ -286,7 +205,7 @@ public:
Imm6 src1_n,
Imm6 src2_n);
bool vmad(ExtVecPredicate pred,
virtual bool vmad(ExtVecPredicate pred,
Imm1 skipinv,
Imm1 gpi1_swiz_ext,
Imm1 opcode2,
@@ -315,7 +234,7 @@ public:
Imm4 src1_swiz,
Imm6 src1_n);
bool vdp(ExtVecPredicate pred,
virtual bool vdp(ExtVecPredicate pred,
Imm1 skipinv,
bool clip_plane_enable,
Imm1 opcode2,
@@ -341,7 +260,7 @@ public:
Imm3 src1_swiz_x,
Imm6 src1_n);
bool vdual(Imm1 comp_count_type,
virtual bool vdual(Imm1 comp_count_type,
Imm1 gpi1_neg,
Imm2 sv_pred,
Imm1 skipinv,
@@ -368,7 +287,7 @@ public:
Imm3 write_mask_non_gpi,
Imm7 unified_store_slot_num);
bool vcomp(ExtPredicate pred,
virtual bool vcomp(ExtPredicate pred,
bool skipinv,
Imm2 dest_type,
bool syncstart,
@@ -387,7 +306,7 @@ public:
Imm7 src1_n,
Imm4 write_mask);
bool vmov(ExtPredicate pred,
virtual bool vmov(ExtPredicate pred,
bool skipinv,
Imm1 test_bit_2,
Imm1 src0_comp_sel,
@@ -412,7 +331,7 @@ public:
Imm6 src1_n,
Imm6 src2_n);
bool vpck(ExtPredicate pred,
virtual bool vpck(ExtPredicate pred,
bool skipinv,
bool nosched,
Imm1 unknown,
@@ -438,7 +357,7 @@ public:
Imm6 src2_n,
Imm1 comp_sel_0_bit0);
bool vtst(ExtPredicate pred,
virtual bool vtst(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
@@ -464,7 +383,7 @@ public:
Imm7 src1_n,
Imm7 src2_n);
bool vtstmsk(ExtPredicate pred,
virtual bool vtstmsk(ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
@@ -489,7 +408,7 @@ public:
Imm7 src1_n,
Imm7 src2_n);
bool vbw(Imm3 op1,
virtual bool vbw(Imm3 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
@@ -513,7 +432,7 @@ public:
Imm7 src1_n,
Imm7 src2_n);
bool sop2(Imm2 pred,
virtual bool sop2(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
@@ -539,9 +458,10 @@ public:
Imm1 asrc1_mod,
Imm1 dest_mod,
Imm7 src1_n,
Imm7 src2_n);
Imm7 src2_n)
= 0;
bool sop2m(Imm2 pred,
virtual bool sop2m(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
@@ -560,9 +480,10 @@ public:
Imm2 src2bank,
Imm7 destnum,
Imm7 src1num,
Imm7 src2num);
Imm7 src2num)
= 0;
bool sop3(Imm2 pred,
virtual bool sop3(Imm2 pred,
Imm1 mod1,
Imm1 skipinv,
Imm1 nosched,
@@ -583,9 +504,10 @@ public:
Imm7 destn,
Imm7 src0n,
Imm7 src1n,
Imm7 src2n);
Imm7 src2n)
= 0;
bool i8mad(Imm2 pred,
virtual bool i8mad(Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
@@ -614,7 +536,7 @@ public:
Imm7 src1_num,
Imm7 src2_num);
bool i16mad(ShortPredicate pred,
virtual bool i16mad(ShortPredicate pred,
Imm1 abs,
Imm1 skipinv,
Imm1 nosched,
@@ -639,7 +561,7 @@ public:
Imm7 src1_n,
Imm7 src2_n);
bool i32mad(ShortPredicate pred,
virtual bool i32mad(ShortPredicate pred,
Imm1 src0_high,
Imm1 nosched,
Imm1 src1_high,
@@ -667,9 +589,9 @@ public:
bool illegal24();
bool i8mad2();
virtual bool i8mad2();
bool i32mad2(ExtPredicate pred,
virtual bool i32mad2(ExtPredicate pred,
Imm1 nosched,
Imm2 sn,
bool dest_bank_ext,
@@ -692,7 +614,7 @@ public:
bool illegal27();
bool smp(ExtPredicate pred,
virtual bool smp(ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 syncstart,
@@ -716,7 +638,7 @@ public:
Imm7 src1_n,
Imm7 src2_n);
bool phas(Imm1 sprvv,
virtual bool phas(Imm1 sprvv,
Imm1 end,
Imm1 imm,
Imm1 src1_bank_ext,
@@ -732,9 +654,9 @@ public:
Imm7 src1_n_or_exe_addr_mid,
Imm7 src2_n_or_exe_addr_low);
bool nop();
virtual bool nop();
bool br(ExtPredicate pred,
virtual bool br(ExtPredicate pred,
Imm1 syncend,
bool exception,
bool pwait,
@@ -747,7 +669,7 @@ public:
Imm1 all_inst,
uint32_t br_off);
bool smlsi(Imm1 nosched,
virtual bool smlsi(Imm1 nosched,
Imm4 temp_limit,
Imm4 pa_limit,
Imm4 sa_limit,
@@ -760,9 +682,9 @@ public:
Imm8 src1_inc,
Imm8 src2_inc);
bool kill(ShortPredicate pred);
virtual bool kill(ShortPredicate pred) = 0;
bool limm(bool skipinv,
virtual bool limm(bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
@@ -771,12 +693,13 @@ public:
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits);
Imm21 imm_value_first_21bits)
= 0;
bool spec(bool special,
virtual bool spec(bool special,
SpecialCategory category);
bool vldst(Imm2 op1,
virtual bool vldst(Imm2 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
@@ -803,55 +726,57 @@ public:
Imm7 src1_n,
Imm7 src2_n);
// Instructions end
private:
spv::Id vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask);
// SPIR-V emitter
spv::Builder &m_b;
protected:
// Instruction word being translated
const uint64_t &m_instr;
// SPIR-V IDs
const SpirvShaderParameters &m_spirv_params;
USSERecompiler &m_recompiler;
const SceGxmProgram &m_program;
const FeatureState &m_features;
bool m_second_program{ false };
// Decoded instruction
// These are used for shared backends
Instruction decoded_inst;
Instruction decoded_inst_2;
std::uint8_t decoded_source_mask;
std::uint8_t decoded_source_mask_2;
std::uint8_t decoded_source_mask_3;
std::uint8_t decoded_dest_mask;
std::uint8_t decoded_dest_mask_2;
};
constexpr int sgx543_pc_bits = 20;
struct USSERecompiler final {
struct USSERecompiler {
const std::uint64_t *inst;
std::size_t count;
spv::Builder &b;
USSETranslatorVisitor visitor;
std::uint64_t cur_instr;
usse::USSEOffset cur_pc;
const SceGxmProgram *program;
spv::Function *end_hook_func;
USSEBlockNode tree_block_node;
std::unique_ptr<USSETranslatorVisitor> visitor;
explicit USSERecompiler(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features,
const SpirvShaderParameters &parameters, utils::SpirvUtilFunctions &utils, spv::Function *end_hook_func,
const NonDependentTextureQueryCallInfos &queries, const spv::Id render_info_id);
explicit USSERecompiler(const SceGxmProgram &program);
void reset(const std::uint64_t *inst, const std::size_t count);
virtual void reset(const std::uint64_t *inst, const std::size_t count);
void compile_code_node(const usse::USSECodeNode &code);
void compile_break_node(const usse::USSEBreakNode &node);
void compile_continue_node(const usse::USSEContinueNode &node);
void compile_conditional_node(const usse::USSEConditionalNode &cond);
void compile_loop_node(const usse::USSELoopNode &loop);
void compile_block(const usse::USSEBlockNode &block);
spv::Id get_condition_value(const std::uint8_t pred, const bool neg = false);
spv::Function *compile_program_function();
virtual void compile_break_node(const usse::USSEBreakNode &node) = 0;
virtual void compile_continue_node(const usse::USSEContinueNode &node) = 0;
virtual void compile_conditional_node(const usse::USSEConditionalNode &cond) = 0;
virtual void compile_loop_node(const usse::USSELoopNode &loop) = 0;
virtual void compile_code_node(const usse::USSECodeNode &code);
virtual void begin_condition(const int cond) = 0;
virtual void end_condition() = 0;
};
} // namespace shader::usse
@@ -32,7 +32,7 @@ namespace usse {
enum class Opcode {
#define OPCODE(n) n,
#include "usse_opcodes.inc"
#include "opcodes.inc"
#undef OPCODE
};
@@ -298,6 +298,8 @@ inline bool is_float_data_type(const DataType dtype) {
return (dtype == DataType::C10) || (dtype == DataType::F16) || (dtype == DataType::F32);
}
size_t dest_mask_to_comp_count(shader::usse::Imm4 dest_mask);
// TODO: Make this a std::set?
enum InstructionFlags {
};
@@ -382,6 +384,8 @@ struct AttributeInputSource {
// resource index
std::uint32_t index;
std::uint16_t semantic;
std::uint32_t opt_location = 0xFFFFFFFF;
bool regformat;
};
@@ -391,7 +395,7 @@ struct LiteralInputSource {
};
struct UniformBufferInputSource {
uint32_t base;
int32_t base;
// resource index
uint32_t index;
};
@@ -401,8 +405,16 @@ struct DependentSamplerInputSource {
uint32_t index; // resource index
};
struct NonDependentSamplerSampleSource {
int sampler_index;
int coord_index;
int coord_load_comp_count;
int proj_pos;
};
// Read source field in Input struct
using InputSource = std::variant<UniformBufferInputSource, LiteralInputSource, AttributeInputSource, DependentSamplerInputSource>;
using InputSource = std::variant<UniformBufferInputSource, LiteralInputSource, AttributeInputSource, DependentSamplerInputSource, NonDependentSamplerSampleSource>;
/**
* Input parameters that are usually copied into PA or SA
@@ -440,6 +452,8 @@ enum class ShaderPhase {
Max,
};
bool is_sub_opcode(Opcode test_op);
} // namespace usse
} // namespace shader
@@ -15,8 +15,8 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_types.h>
#include <shader/decoder_helpers.h>
#include <shader/types.h>
#include <util/log.h>
@@ -371,6 +371,8 @@ Operand &decode_dest(usse::Operand &dest, Imm6 dest_n, Imm2 dest_bank, bool bank
dest.num = dest_n;
dest.bank = decode_dest_bank(dest_bank, bank_ext);
dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
dest.flags = static_cast<RegisterFlags>(0);
dest.index = 3;
if (is_double_regs)
double_reg(dest.num, dest.bank);
@@ -383,6 +385,7 @@ Operand &decode_src12(usse::Operand &src, Imm6 src_n, Imm2 src_bank_sel, Imm1 sr
src.bank = decode_src12_bank(src_bank_sel, src_bank_ext);
src.num = src_n;
src.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
src.flags = static_cast<RegisterFlags>(0);
if (is_double_regs)
double_reg(src.num, src.bank);
@@ -396,6 +399,7 @@ usse::Operand &decode_src0(usse::Operand &src, usse::Imm6 src_n, usse::Imm1 src_
src.num = src_n;
src.bank = decode_src0_bank(src_bank_sel, src_bank_ext);
src.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
src.flags = static_cast<RegisterFlags>(0);
if (is_double_regs)
double_reg(src.num, src.bank);
@@ -15,9 +15,8 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <string>
#include <unordered_map>
@@ -35,7 +34,7 @@ std::string *disasm_storage = nullptr;
const std::string &opcode_str(const Opcode &e) {
static const std::unordered_map<Opcode, const std::string> names = {
#define OPCODE(n) { Opcode::n, std::string(#n) },
#include "shader/usse_opcodes.inc"
#include "shader/opcodes.inc"
#undef OPCODE
};
return names.at(e);
+93
View File
@@ -0,0 +1,93 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/glsl/code_writer.h>
namespace shader::usse::glsl {
CodeWriter::CodeWriter()
: active_body_type(BODY_TYPE_MAIN) {
preload_indentation = "\t";
postwork_indentation = "\t";
for (std::size_t i = 0; i < BODY_TYPE_MAX; i++) {
body_indentation[i] = "\t";
}
}
void CodeWriter::add_declaration(const std::string &decl_line) {
decl_content += decl_indentation + decl_line + "\n";
}
void CodeWriter::add_to_preload(const std::string &line) {
preload_content += preload_indentation + line + "\n";
}
void CodeWriter::add_to_current_body(const std::string &line) {
body_content[active_body_type] += body_indentation[active_body_type] + line + "\n";
}
void CodeWriter::indent_preload() {
preload_indentation.push_back('\t');
}
void CodeWriter::dedent_preload() {
if (preload_indentation.length() > 1) {
preload_indentation.pop_back();
}
}
void CodeWriter::indent_declaration() {
decl_indentation.push_back('\t');
}
void CodeWriter::dedent_declaration() {
decl_indentation.pop_back();
}
void CodeWriter::indent_current_body() {
body_indentation[active_body_type].push_back('\t');
}
void CodeWriter::dedent_current_body() {
if (body_indentation[active_body_type].length() > 1) {
body_indentation[active_body_type].pop_back();
}
}
void CodeWriter::set_active_body_type(const CodeBodyType type) {
active_body_type = type;
}
std::string CodeWriter::assemble() {
std::string adjusted_main = std::string("void main() {\n") + preload_content;
adjusted_main += body_content[BODY_TYPE_MAIN];
if (!body_content[BODY_TYPE_SECONDARY_PROGRAM].empty()) {
adjusted_main += "\tsecondary_program();\n";
}
adjusted_main += "\tprimary_program();\n";
adjusted_main += body_content[BODY_TYPE_POSTWORK];
adjusted_main += "}\n";
std::string adjusted_secondary;
if (!body_content[BODY_TYPE_SECONDARY_PROGRAM].empty()) {
adjusted_secondary = std::string("void secondary_program() {\n") + body_content[BODY_TYPE_SECONDARY_PROGRAM] + "}\n";
}
std::string adjusted_primary = std::string("void primary_program() {\n") + body_content[BODY_TYPE_PRIMARY_PROGRAM] + "}\n";
return decl_content + "\n" + adjusted_secondary + "\n" + adjusted_primary + "\n" + adjusted_main;
}
} // namespace shader::usse::glsl
File diff suppressed because it is too large Load Diff
+619
View File
@@ -0,0 +1,619 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <features/state.h>
#include <gxm/functions.h>
#include <gxm/types.h>
#include <shader/disasm.h>
#include <shader/glsl/code_writer.h>
#include <shader/glsl/consts.h>
#include <shader/glsl/params.h>
#include <shader/glsl/recompiler.h>
#include <shader/glsl/translator.h>
#include <shader/gxp_parser.h>
#include <shader/recompiler.h>
#include <shader/types.h>
#include <util/log.h>
#include <util/overloaded.h>
namespace shader::usse::glsl {
static void nicen_name_for_glsl_rules(std::string &prev) {
while (true) {
std::size_t dot_pos = prev.find('.');
if (dot_pos != std::string::npos) {
prev.replace(dot_pos, 1, "_");
} else {
break;
}
}
while (true) {
std::size_t bracket_open_pos = prev.find('[');
if (bracket_open_pos != std::string::npos) {
prev.replace(bracket_open_pos, 1, "_");
} else {
break;
}
}
while (true) {
std::size_t bracket_open_pos = prev.find(']');
if (bracket_open_pos != std::string::npos) {
prev.replace(bracket_open_pos, 1, "_");
} else {
break;
}
}
// GLSL does not allow name with double dash inside
while (true) {
std::size_t pos_double_dash = prev.find("__");
if (pos_double_dash != std::string::npos) {
prev.replace(pos_double_dash, 2, "_dd_");
} else {
break;
}
}
}
static void create_uniform_buffers(CodeWriter &writer, const SceGxmProgram &program, const ProgramInput &input, std::map<int, int> &buffer_bases) {
std::map<int, std::uint32_t> buffer_sizes;
for (const auto &buffer : input.uniform_buffers) {
const auto buffer_size = (buffer.size + 3) / 4;
buffer_sizes.emplace(buffer.index, buffer_size);
}
if (buffer_sizes.empty()) {
return;
}
writer.add_declaration(fmt::format("layout (std140, binding = {}) buffer {}Type {{", program.is_vertex() ? 0 : 1, program.is_vertex() ? VERTEX_UB_GROUP_NAME : FRAGMENT_UB_GROUP_NAME));
std::uint32_t last_offset = 0;
for (auto [buffer_index, buffer_size] : buffer_sizes) {
writer.add_declaration(fmt::format("\tvec4 buffer{}[{}];", buffer_index, buffer_size));
buffer_bases.emplace(buffer_index, last_offset);
last_offset += buffer_size * 16;
}
writer.add_declaration(fmt::format("}} {};\n", program.is_vertex() ? VERTEX_UB_GROUP_NAME : FRAGMENT_UB_GROUP_NAME));
}
static void create_samplers(CodeWriter &writer, SamplerMap &sampler_map, const SceGxmProgram &program, const ProgramInput &input) {
writer.add_declaration("vec4 textureProjCube(samplerCube sampler, vec4 coord) {");
writer.add_declaration("\treturn texture(sampler, coord.xyz / coord.w);");
writer.add_declaration("}");
for (const auto &sampler : input.samplers) {
std::string sampler_name = fmt::format("{}_{}", program.is_vertex() ? "vertTex" : "fragTex", sampler.name);
nicen_name_for_glsl_rules(sampler_name);
writer.add_declaration(fmt::format("layout (binding = {}) uniform {} {};", sampler.index + (program.is_vertex() ? SCE_GXM_MAX_TEXTURE_UNITS : 0),
sampler.is_cube ? "samplerCube" : "sampler2D", sampler_name));
sampler_map.emplace(sampler.index, std::make_pair(sampler_name, sampler.is_cube));
}
}
static void create_fragment_inputs(CodeWriter &writer, ShaderVariables &variables, const FeatureState &features, const SceGxmProgram &program) {
writer.add_declaration(fmt::format("layout (binding = {}, rgba8) uniform image2D f_mask;", MASK_TEXTURE_SLOT_IMAGE));
writer.add_to_preload("if (all(lessThan(imageLoad(f_mask, ivec4(gl_FragCoord).xy), vec4(0.5)))) discard;");
if (program.is_frag_color_used()) {
if (features.direct_fragcolor) {
// The GPU supports gl_LastFragData. It's only OpenGL though
if (features.preserve_f16_nan_as_u16) {
writer.add_to_preload("if (renderFragInfo.use_raw_image >= 0.5) {");
writer.indent_preload();
writer.add_to_preload("o0 = gl_LastFragData[1].xyzw;");
writer.dedent_preload();
writer.add_to_preload("} else {");
writer.indent_preload();
writer.add_to_preload("o0 = gl_LastFragData[0];");
writer.dedent_preload();
writer.add_to_preload("}");
} else {
writer.add_to_preload("o0 = gl_LastFragData[0];");
}
} else if (features.support_shader_interlock || features.support_texture_barrier) {
writer.add_declaration(fmt::format("layout (binding = {}, rgba8) uniform image2D f_colorAttachment;", COLOR_ATTACHMENT_TEXTURE_SLOT_IMAGE));
writer.add_declaration("");
variables.should_gen_pack_unpack[ShaderVariables::GEN_PACK_U8] = true;
variables.should_gen_pack_unpack[ShaderVariables::GEN_PACK_U16] = true;
if (features.preserve_f16_nan_as_u16) {
writer.add_declaration(fmt::format("layout (binding = {}, rgba16ui) uniform uimage2D f_colorAttachment_rawUI;", COLOR_ATTACHMENT_RAW_TEXTURE_SLOT_IMAGE));
writer.add_to_preload("if (renderFragInfo.use_raw_image >= 0.5) {");
writer.indent_preload();
writer.add_to_preload("uvec4 sampled = imageLoad(f_colorAttachment_rawUI, ivec2(gl_FragCoord.xy));");
writer.add_to_preload("o0.xy = vec2(pack2xU16(sampled.xy), pack2xU16(sampled.zw));");
writer.dedent_preload();
writer.add_to_preload("} else {");
writer.indent_preload();
writer.add_to_preload("o0.x = pack4xU8(uvec4(imageLoad(f_colorAttachment, ivec2(gl_FragCoord.xy)) * vec4(255.0)));");
writer.dedent_preload();
writer.add_to_preload("}");
} else {
writer.add_to_preload("o0.x = pack4xU8(uvec4(imageLoad(f_colorAttachment, ivec2(gl_FragCoord.xy)) * vec4(255.0)));");
}
variables.mark_f32_raw_dirty(RegisterBank::OUTPUT, 0);
variables.mark_f32_raw_dirty(RegisterBank::OUTPUT, 1);
} else {
// Try to initialize outs[0] to some nice value. In case the GPU has garbage data for our shader
writer.add_to_preload("o0 = vec4(0.0);");
}
}
}
static void create_parameters(ProgramState &state, CodeWriter &writer, ShaderVariables &params, const FeatureState &features, const SceGxmProgram &program, const ProgramInput &program_input,
const std::vector<SceGxmVertexAttribute> *hint_attributes = nullptr) {
if (program.is_fragment()) {
writer.add_to_preload("if ((renderFragInfo.front_disabled != 0.0) && gl_FrontFacing) discard;");
writer.add_to_preload("if ((renderFragInfo.back_disabled != 0.0) && !gl_FrontFacing) discard;");
}
writer.add_declaration("bool p0;");
writer.add_declaration("bool p1;");
writer.add_declaration("bool p2;");
writer.add_declaration("float temp1;");
writer.add_declaration("vec2 temp2;");
writer.add_declaration("vec3 temp3;");
writer.add_declaration("vec4 temp4;");
writer.add_declaration("float temp1_1;");
writer.add_declaration("vec2 temp2_1;");
writer.add_declaration("vec3 temp3_1;");
writer.add_declaration("vec4 temp4_1;");
writer.add_declaration("float temp1_2;");
writer.add_declaration("vec2 temp2_2;");
writer.add_declaration("vec3 temp3_2;");
writer.add_declaration("vec4 temp4_2;");
writer.add_declaration("int itemp1;");
writer.add_declaration("ivec2 itemp2;");
writer.add_declaration("ivec3 itemp3;");
writer.add_declaration("ivec4 itemp4;");
writer.add_declaration("int base_temp;");
writer.add_declaration("\n");
std::map<int, int> buffer_bases;
SamplerMap sampler_map_on_binding;
std::vector<VarToReg> var_to_regs;
create_uniform_buffers(writer, program, program_input, buffer_bases);
create_samplers(writer, sampler_map_on_binding, program, program_input);
std::uint32_t in_fcount_allocated = 0;
for (const auto &input : program_input.inputs) {
std::visit(overloaded{
[&](const LiteralInputSource &s) {
params.mark_f32_raw_dirty(RegisterBank::SECATTR, input.offset);
writer.add_to_current_body(fmt::format("sa{}.{} = uintBitsToFloat(0x{:X});", input.offset / 4 * 4, static_cast<char>('w' + ((input.offset + 1) % 4)),
*reinterpret_cast<const std::uint32_t *>(&s.data)));
},
[&](const UniformBufferInputSource &s) {
const int base = buffer_bases.at(s.index) + s.base;
params.mark_f32_raw_dirty(RegisterBank::SECATTR, input.offset);
writer.add_to_current_body(fmt::format("sa{}.{} = intBitsToFloat({});", static_cast<std::int32_t>(input.offset) / 4 * 4, static_cast<char>('w' + ((input.offset + 1) % 4)), base));
},
[&](const DependentSamplerInputSource &s) {
const auto sampler_name = sampler_map_on_binding.at(s.index);
state.samplers_on_offset.emplace(input.offset, sampler_name);
},
[&](const AttributeInputSource &s) {
if (input.bank == RegisterBank::SPECIAL) {
// Texcoord base location
writer.add_declaration(fmt::format("layout (location = {}) in vec4 v_TexCoord{};", s.opt_location + 4, s.index));
} else {
VarToReg store_info;
store_info.offset = input.offset;
store_info.comp_count = input.array_size * input.component_count;
store_info.reg_format = s.regformat;
store_info.var_name = s.name;
store_info.location = (s.opt_location != 0xFFFFFFFF) ? s.opt_location : in_fcount_allocated / 4;
store_info.builtin = false;
// Some compilers does not allow in variables to be casted
switch (s.semantic) {
case SCE_GXM_PARAMETER_SEMANTIC_INDEX:
writer.add_to_preload("int vertexIdTemp = gl_VertexID;");
store_info.var_name = "intBitsToFloat(vertexIdTemp)";
store_info.data_type = static_cast<int>(DataType::F32);
store_info.comp_count = 1;
store_info.builtin = true;
break;
case SCE_GXM_PARAMETER_SEMANTIC_INSTANCE:
writer.add_to_preload("int instanceIdTemp = gl_InstanceID;");
store_info.var_name = "intBitsToFloat(instanceIdTemp)";
store_info.data_type = static_cast<int>(DataType::F32);
store_info.comp_count = 1;
store_info.builtin = true;
break;
default:
store_info.data_type = static_cast<int>(input.type);
}
var_to_regs.push_back(store_info);
in_fcount_allocated += ((input.array_size * input.component_count + 3) / 4 * 4);
}
},
[&](const NonDependentSamplerSampleSource &s) {
NonDependentTextureQueryInfo info;
info.sampler_name = sampler_map_on_binding[s.sampler_index].first;
info.sampler_cube = sampler_map_on_binding[s.sampler_index].second;
info.sampler_index = s.sampler_index;
info.coord_index = s.coord_index;
info.proj_pos = s.proj_pos;
info.data_type = static_cast<int>(input.type);
info.offset_in_pa = input.offset;
state.non_dependent_queries.push_back(info);
} },
input.source);
}
if ((in_fcount_allocated == 0) && (program.primary_reg_count != 0)) {
// Using hint to create attribute. Looks like attribute with F32 types are stripped, otherwise
// whole shader symbols are kept...
if (hint_attributes) {
LOG_INFO("Shader stripped all symbols, trying to use hint attributes");
for (std::size_t i = 0; i < hint_attributes->size(); i++) {
VarToReg store_info;
store_info.offset = hint_attributes->at(i).regIndex;
store_info.comp_count = (hint_attributes->at(i).componentCount + 3) / 4 * 4;
store_info.reg_format = false;
store_info.var_name = fmt::format("attribute{}", i);
var_to_regs.push_back(store_info);
}
}
}
// Store var to regs
for (auto &var_to_reg : var_to_regs) {
nicen_name_for_glsl_rules(var_to_reg.var_name);
if (!var_to_reg.builtin) {
writer.add_declaration(fmt::format("layout (location = {}) in vec4 {};", var_to_reg.location, var_to_reg.var_name));
}
Operand dest;
dest.num = var_to_reg.offset;
dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
dest.bank = RegisterBank::PRIMATTR;
dest.type = static_cast<DataType>(var_to_reg.data_type);
if (is_integer_data_type(dest.type)) {
var_to_reg.var_name = fmt::format("{}vec4({})", (is_signed_integer_data_type(dest.type) ? "i" : "u"), var_to_reg.var_name);
}
if (var_to_reg.comp_count == 4) {
params.store(dest, var_to_reg.var_name, 0b1111, 0, true);
} else {
std::string swizz_str = std::string(".xyzw").substr(0, var_to_reg.comp_count + 1);
params.store(dest, var_to_reg.var_name + swizz_str, 0b1111 >> (4 - var_to_reg.comp_count), 0, true);
}
}
if (program.is_vertex()) {
// Add vertex render info
writer.add_declaration("layout (std140, binding = 2) uniform GxmRenderVertBufferBlock {");
writer.add_declaration("\tvec4 viewport_flip;");
writer.add_declaration("\tfloat viewport_flag;");
writer.add_declaration("\tfloat screen_width;");
writer.add_declaration("\tfloat screen_height;");
writer.add_declaration("\tvec4 integral_query_formats[4];");
writer.add_declaration("} renderVertInfo;");
} else {
// Add fragment render info
writer.add_declaration("layout (std140, binding = 3) uniform GxmRenderFragBufferBlock {");
writer.add_declaration("\tfloat back_disabled;");
writer.add_declaration("\tfloat front_disabled;");
writer.add_declaration("\tfloat writing_mask;");
writer.add_declaration("\tfloat use_raw_image;");
writer.add_declaration("\tvec4 integral_query_formats[4];");
writer.add_declaration("} renderFragInfo;");
create_fragment_inputs(writer, params, features, program);
}
writer.add_declaration("\n");
}
struct VertexProgramOutputProperties {
std::string name;
std::uint32_t component_count;
std::uint32_t location;
VertexProgramOutputProperties()
: name(nullptr)
, component_count(0)
, location(0) {}
VertexProgramOutputProperties(const char *name, std::uint32_t component_count, std::uint32_t location)
: name(name)
, component_count(component_count)
, location(location) {}
};
using VertexProgramOutputPropertiesMap = std::map<SceGxmVertexProgramOutputs, VertexProgramOutputProperties>;
static void create_output(ProgramState &state, CodeWriter &writer, ShaderVariables &params, const FeatureState &features, const SceGxmProgram &program) {
if (program.is_vertex()) {
gxp::GxmVertexOutputTexCoordInfos coord_infos;
SceGxmVertexProgramOutputs vertex_outputs = gxp::get_vertex_outputs(program, &coord_infos);
static const auto calculate_copy_comp_count = [](uint8_t info) {
// TexCoord info uses preset values described below for determining lengths.
uint8_t length = 0;
if (info & 0b001u)
length += 2; // uses xy
if (info & 0b010u)
length += 1; // uses z
if (info & 0b100u)
length += 1; // uses w
return length;
};
VertexProgramOutputPropertiesMap vertex_properties_map;
// list is used here to gurantee the vertex outputs are written in right order
std::list<SceGxmVertexProgramOutputs> vertex_outputs_list;
const auto add_vertex_output_info = [&](SceGxmVertexProgramOutputs vo, const char *name, std::uint32_t component_count, std::uint32_t location) {
vertex_properties_map.emplace(vo, VertexProgramOutputProperties(name, component_count, location));
vertex_outputs_list.push_back(vo);
};
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_POSITION, "v_Position", 4, 0);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_COLOR0, "v_Color0", 4, 1);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_COLOR1, "v_Color1", 4, 2);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_FOG, "v_Fog", 2, 3);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD0, "v_TexCoord0", calculate_copy_comp_count(coord_infos[0]), 4);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD1, "v_TexCoord1", calculate_copy_comp_count(coord_infos[1]), 5);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD2, "v_TexCoord2", calculate_copy_comp_count(coord_infos[2]), 6);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD3, "v_TexCoord3", calculate_copy_comp_count(coord_infos[3]), 7);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD4, "v_TexCoord4", calculate_copy_comp_count(coord_infos[4]), 8);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD5, "v_TexCoord5", calculate_copy_comp_count(coord_infos[5]), 9);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD6, "v_TexCoord6", calculate_copy_comp_count(coord_infos[6]), 10);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD7, "v_TexCoord7", calculate_copy_comp_count(coord_infos[7]), 11);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD8, "v_TexCoord8", calculate_copy_comp_count(coord_infos[8]), 12);
add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_TEXCOORD9, "v_TexCoord9", calculate_copy_comp_count(coord_infos[9]), 13);
// TODO: this should be translated to gl_PointSize
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_PSIZE, "v_Psize", 1);
// TODO: these should be translated to gl_ClipDistance
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP0, "v_Clip0", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP1, "v_Clip1", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP2, "v_Clip2", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP3, "v_Clip3", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP4, "v_Clip4", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP5, "v_Clip5", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP6, "v_Clip6", 1);
// add_vertex_output_info(SCE_GXM_VERTEX_PROGRAM_OUTPUT_CLIP7, "v_Clip7", 1);
Operand o_load;
o_load.bank = RegisterBank::OUTPUT;
o_load.num = 0;
o_load.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
o_load.type = DataType::F32;
for (const auto vo : vertex_outputs_list) {
if (vertex_outputs & vo) {
const auto vo_typed = static_cast<SceGxmVertexProgramOutputs>(vo);
VertexProgramOutputProperties properties = vertex_properties_map.at(vo_typed);
std::string param_loaded = params.load(o_load, 0b1111, 0);
if (vo_typed == SCE_GXM_VERTEX_PROGRAM_OUTPUT_POSITION) {
writer.add_to_current_body(fmt::format("gl_Position = {} * renderVertInfo.viewport_flip;", param_loaded));
writer.add_to_current_body("if (renderVertInfo.viewport_flag < 0.5) {");
writer.indent_current_body();
writer.add_to_current_body("gl_Position.xy = gl_Position.xy * vec2(2.0 / renderVertInfo.screen_width, -2.0 / renderVertInfo.screen_height) + vec2(-1.0, 1.0);");
writer.add_to_current_body("gl_Position.z = gl_Position.w;");
writer.dedent_current_body();
// if (!features.spirv_shader) {
// If we throw it to GL, we must adjust the Z clip space from 0.1 in vita to -1.1!
// We use the same formula as RPCS3: (z * 2) - w
writer.add_to_current_body("} else {");
writer.indent_current_body();
writer.add_to_current_body("gl_Position.z = (gl_Position.z + gl_Position.z) - gl_Position.w;");
writer.dedent_current_body();
//}
writer.add_to_current_body("}");
} else {
writer.add_declaration(fmt::format("layout (location = {}) out vec4 {};", properties.location, properties.name));
writer.add_to_current_body(fmt::format("{} = {};", properties.name, param_loaded));
}
o_load.num += properties.component_count;
}
}
} else {
Operand color_val_operand;
color_val_operand.bank = program.is_native_color() ? RegisterBank::OUTPUT : RegisterBank::PRIMATTR;
color_val_operand.num = 0;
color_val_operand.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
color_val_operand.type = std::get<0>(shader::get_parameter_type_store_and_name(program.get_fragment_output_type()));
auto vertex_varyings_ptr = program.vertex_varyings();
if (!program.is_native_color() && vertex_varyings_ptr->output_param_type == 1) {
color_val_operand.num = vertex_varyings_ptr->fragment_output_start;
}
std::string result = params.load(color_val_operand, 0b1111, 0);
if (is_unsigned_integer_data_type(color_val_operand.type)) {
result = fmt::format("vec4(uvec4({})) / 255.0", result);
}
bool use_outs = true;
if (program.is_frag_color_used()) {
if (features.is_programmable_blending_need_to_bind_color_attachment()) {
writer.add_to_current_body(fmt::format("imageStore(f_colorAttachment, ivec2(gl_FragCoord.xy), {});", result));
if (features.preserve_f16_nan_as_u16) {
color_val_operand.type = DataType::UINT16;
result = params.load(color_val_operand, 0b1111, 0);
writer.add_to_current_body(fmt::format("imageStore(f_colorAttachment_rawUI, ivec2(gl_FragCoord.xy), {});", result));
}
use_outs = false;
}
}
if (use_outs) {
writer.add_declaration("layout (location = 0) out vec4 out_color;");
writer.add_to_current_body(fmt::format("out_color = {};", result));
if (features.preserve_f16_nan_as_u16) {
color_val_operand.type = DataType::UINT16;
result = params.load(color_val_operand, 0b1111, 0);
writer.add_declaration("layout (location = 1) out uvec4 out_color_ui;");
writer.add_to_current_body(fmt::format("out_color_ui = {};", result));
}
}
}
writer.add_declaration("\n");
}
static void create_neccessary_headers(CodeWriter &writer, const SceGxmProgram &program, const FeatureState &features, const std::string &shader_hash) {
// Must at least be version 430
writer.add_declaration("#version 430");
writer.add_declaration("");
writer.add_declaration(std::string("// Shader hash: ") + shader_hash);
if (program.is_fragment() && program.is_frag_color_used()) {
if (features.direct_fragcolor) {
writer.add_declaration("#extension GL_EXT_shader_framebuffer_fetch: require");
} else if (features.should_use_shader_interlock()) {
writer.add_declaration("#extension GL_ARB_fragment_shader_interlock: require");
writer.add_to_preload("beginInvocationInterlockARB();");
}
}
writer.add_declaration("");
if (program.is_fragment() && program.is_frag_color_used() && !features.direct_fragcolor) {
writer.add_declaration("layout(early_fragment_tests) in;");
}
}
static void create_neccessary_footers(CodeWriter &writer, const SceGxmProgram &program, const FeatureState &features) {
if (program.is_fragment() && program.is_frag_color_used()) {
if (features.should_use_shader_interlock()) {
writer.add_to_current_body("endInvocationInterlockARB();");
}
}
}
static void create_program_needed_functions(const ProgramState &state, const ProgramInput &input, CodeWriter &writer) {
if (state.should_generate_vld_func) {
writer.add_declaration("float fetchMemory(int offset) {");
writer.indent_declaration();
const char *name_buffer_glob = (state.actual_program.is_vertex()) ? VERTEX_UB_GROUP_NAME : FRAGMENT_UB_GROUP_NAME;
std::uint32_t base = 0;
for (std::size_t i = 0; i < input.uniform_buffers.size(); i++) {
std::uint32_t real_size = static_cast<std::uint32_t>((input.uniform_buffers[i].size + 3) / 4 * 16);
writer.add_declaration(fmt::format("if ((offset >= {}) && (offset < {})) {{", base, base + real_size));
writer.indent_declaration();
if (base != 0)
writer.add_declaration(fmt::format("offset -= {};", base));
writer.add_declaration("int vec_index = offset / 16;");
writer.add_declaration("int bytes_in_vec = offset - vec_index * 16;");
writer.add_declaration("int comp_in_vec = bytes_in_vec / 4;");
writer.add_declaration("int start_byte_in_comp = bytes_in_vec - comp_in_vec * 4;");
writer.add_declaration("int lshift_amount = 4 - start_byte_in_comp;");
writer.add_declaration("int next_comp = comp_in_vec + 1;");
writer.add_declaration("bool aligned = (lshift_amount == 4);");
writer.add_declaration(fmt::format("return uintBitsToFloat((floatBitsToUint({}.buffer{}[vec_index][comp_in_vec]) << uint(start_byte_in_comp * 8))"
" | (aligned ? 0u : (floatBitsToUint({}.buffer{}[vec_index + (next_comp / 4)][next_comp - (next_comp / 4) * 4]) >> uint((aligned ? 0 : lshift_amount) * 8))));",
name_buffer_glob, input.uniform_buffers[i].index, name_buffer_glob, input.uniform_buffers[i].index));
writer.dedent_declaration();
writer.add_declaration("}");
base += real_size;
}
writer.add_declaration("return 0.0;");
writer.dedent_declaration();
writer.add_declaration("}\n");
}
}
} // namespace shader::usse::glsl
namespace shader {
std::string convert_gxp_to_glsl(const SceGxmProgram &program, const std::string &shader_hash, const FeatureState &features,
const std::vector<SceGxmVertexAttribute> *hint_attributes, bool maskupdate, bool force_shader_debug,
std::function<bool(const std::string &ext, const std::string &dump)> dumper) {
usse::glsl::ProgramState program_state{ program };
usse::glsl::CodeWriter code_writer;
usse::ProgramInput inputs = shader::get_program_input(program);
usse::glsl::ShaderVariables variables(code_writer, inputs, program.is_vertex());
std::string disasm_dump;
usse::disasm::disasm_storage = &disasm_dump;
usse::glsl::create_neccessary_headers(code_writer, program, features, shader_hash);
usse::glsl::create_parameters(program_state, code_writer, variables, features, program, inputs, hint_attributes);
usse::glsl::USSERecompilerGLSL recomp(program_state, code_writer, variables, features);
if (program.secondary_program_instr_count) {
recomp.visitor->set_secondary_program(true);
recomp.reset(program.secondary_program_start(), program.secondary_program_instr_count);
recomp.compile_to_function();
}
recomp.visitor->set_secondary_program(false);
recomp.reset(program.primary_program_start(), program.primary_program_instr_count);
recomp.compile_to_function();
code_writer.set_active_body_type(usse::glsl::BODY_TYPE_POSTWORK);
usse::glsl::create_output(program_state, code_writer, variables, features, program);
usse::glsl::create_neccessary_footers(code_writer, program, features);
code_writer.set_active_body_type(usse::glsl::BODY_TYPE_MAIN);
usse::glsl::create_program_needed_functions(program_state, inputs, code_writer);
variables.generate_helper_functions();
std::string final_result = code_writer.assemble();
if (dumper) {
dumper(program.is_vertex() ? "vert" : "frag", final_result);
dumper("dsm", disasm_dump);
}
LOG_INFO("GLSL Code:\n {}", final_result);
return final_result;
}
} // namespace shader
+136
View File
@@ -0,0 +1,136 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/glsl/code_writer.h>
#include <shader/glsl/params.h>
#include <shader/glsl/translator.h>
#include <util/log.h>
namespace shader::usse::glsl {
USSERecompilerGLSL::USSERecompilerGLSL(ProgramState &program_state, CodeWriter &writer, ShaderVariables &variables, const FeatureState &features)
: USSERecompiler(program_state.actual_program)
, writer(writer)
, variables(variables)
, cond_stack(0) {
visitor = std::make_unique<USSETranslatorVisitorGLSL>(*this, program_state, writer, variables, features, cur_instr, false);
}
std::string USSERecompilerGLSL::get_condition(std::uint8_t num, bool do_neg) {
const ExtPredicate predicator = static_cast<ExtPredicate>(num);
if (predicator >= ExtPredicate::P0 && predicator <= ExtPredicate::P3) {
num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::P0);
} else if (predicator >= ExtPredicate::NEGP0 && predicator <= ExtPredicate::NEGP1) {
num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::NEGP0);
do_neg = !do_neg;
}
if (do_neg) {
return fmt::format("!p{}", num);
}
return fmt::format("p{}", num);
}
void USSERecompilerGLSL::compile_break_node(const usse::USSEBreakNode &node) {
if (node.get_condition() != 0) {
writer.add_to_current_body(fmt::format("if ({}) {{", get_condition(node.get_condition(), false)));
writer.indent_current_body();
}
writer.add_to_current_body("break;");
if (node.get_condition() != 0) {
writer.dedent_current_body();
writer.add_to_current_body("}");
}
}
void USSERecompilerGLSL::compile_continue_node(const usse::USSEContinueNode &node) {
if (node.get_condition() != 0) {
writer.add_to_current_body(fmt::format("if ({}) {{", get_condition(node.get_condition(), false)));
writer.indent_current_body();
}
writer.add_to_current_body("\tcontinue;");
if (node.get_condition() != 0) {
writer.dedent_current_body();
writer.add_to_current_body("}");
}
}
void USSERecompilerGLSL::compile_conditional_node(const usse::USSEConditionalNode &cond) {
writer.add_to_current_body(fmt::format("if ({}) {{", get_condition(cond.negif_condition(), true)));
writer.indent_current_body();
compile_block(*cond.if_block());
if (cond.else_block()) {
writer.dedent_current_body();
writer.add_to_current_body("} else {");
writer.indent_current_body();
compile_block(*cond.else_block());
}
writer.dedent_current_body();
writer.add_to_current_body("}");
}
void USSERecompilerGLSL::compile_loop_node(const usse::USSELoopNode &loop) {
writer.add_to_current_body("while (true) {");
writer.indent_current_body();
compile_block(*loop.content_block());
writer.dedent_current_body();
writer.add_to_current_body("}");
}
void USSERecompilerGLSL::begin_condition(const int cond) {
writer.add_to_current_body(fmt::format("if ({}) {{", get_condition(cond, false)));
writer.indent_current_body();
cond_stack++;
}
void USSERecompilerGLSL::end_condition() {
if (cond_stack > 0) {
writer.dedent_current_body();
writer.add_to_current_body("}");
cond_stack--;
}
}
void USSERecompilerGLSL::compile_code_node(const usse::USSECodeNode &code) {
USSERecompiler::compile_code_node(code);
variables.flush();
}
void USSERecompilerGLSL::compile_to_function() {
if (visitor->is_translating_secondary_program()) {
writer.set_active_body_type(BODY_TYPE_SECONDARY_PROGRAM);
} else {
writer.set_active_body_type(BODY_TYPE_PRIMARY_PROGRAM);
}
compile_block(tree_block_node);
writer.set_active_body_type(BODY_TYPE_MAIN);
}
} // namespace shader::usse::glsl
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/glsl/code_writer.h>
#include <shader/glsl/params.h>
#include <shader/glsl/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
using namespace glsl;
std::string USSETranslatorVisitorGLSL::vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask) {
const DataType load_data_type = inst.opr.src1.type;
const DataType store_data_type = inst.opr.dest.type;
static const std::string compare_functions[2][3] = {
{ "notEqual,",
"lessThan",
"greaterThan" },
{ "equal",
"lessThan",
"greaterThan" }
};
static const std::string compare_ops[2][3] = {
{ "!=",
"<",
">" },
{ "==",
"<=",
">=" }
};
// Zero test number:
// 0 - alway pass
// 1 - zero
// 2 - non-zero
const bool compare_include_equal = (zero_test == 1);
// Optimize this case. Alternative name is CMP.
const char *tb_comp_str[2][4] = {
{ "ne",
"lt",
"gt",
"inv" },
{ "equal",
"le",
"ge",
"inv" }
};
const char *used_comp_str = tb_comp_str[compare_include_equal][sign_test];
std::size_t num_comp_count = dest_mask_to_comp_count(load_mask);
std::string lhs;
std::string rhs;
if (is_sub_opcode(inst.opcode)) {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), "CMPMSK", used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask),
disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), "CMP", used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = variables.load(inst.opr.src1, load_mask, 0);
rhs = variables.load(inst.opr.src2, load_mask, 0);
} else {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}zero.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}zero.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = do_alu_op(inst, load_mask, mask ? mask : 0b1);
if (num_comp_count > 1) {
if (is_unsigned_integer_data_type(load_data_type)) {
rhs = fmt::format("uvec{}(0)", num_comp_count);
} else if (is_signed_integer_data_type(load_data_type)) {
rhs = fmt::format("ivec{}(0)", num_comp_count);
} else {
rhs = fmt::format("vec{}(0.0)", num_comp_count);
}
} else {
rhs = "0";
}
}
if (lhs.empty() || rhs.empty()) {
LOG_ERROR("Source not loaded (lhs: {}, rhs: {})", lhs, rhs);
return "";
}
if (num_comp_count > 1) {
return fmt::format("{}({}, {})", compare_functions[compare_include_equal][sign_test], lhs, rhs);
}
return fmt::format("({}) {} {}", lhs, compare_ops[compare_include_equal][sign_test], rhs);
}
bool USSETranslatorVisitorGLSL::vtst(
ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 src1_neg,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm3 chan_cc,
Imm2 pdst_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vtst(pred, skipinv, onceonly, syncstart, dest_ext, src1_neg, src1_ext, src2_ext,
prec, src2_vscomp, rpt_count, sign_test, zero_test, test_crcomb_and, chan_cc, pdst_n, dest_bank,
src1_bank, src2_bank, dest_n, test_wben, alu_sel, alu_op, src1_n, src2_n)) {
return false;
}
writer.add_to_current_body(fmt::format("p{} = {};", decoded_inst.opr.dest.num, vtst_impl(decoded_inst, pred, zero_test, sign_test, decoded_source_mask, false)));
return true;
}
bool USSETranslatorVisitorGLSL::vtstmsk(
ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 test_flag_2,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm2 tst_mask_type,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vtstmsk(pred, skipinv, onceonly, syncstart, dest_ext, test_flag_2, src1_ext,
src2_ext, prec, src2_vscomp, rpt_count, sign_test, zero_test, test_crcomb_and, tst_mask_type,
dest_bank, src1_bank, src2_bank, dest_n, test_wben, alu_sel, alu_op, src1_n, src2_n)) {
return false;
}
std::string compare_result = vtst_impl(decoded_inst, pred, zero_test, sign_test, 0b1111, true);
switch (decoded_inst.opr.dest.type) {
case DataType::UINT8:
compare_result = fmt::format("uvec4({}) * 255", compare_result);
break;
// TODO: If we support half4 then must convert to half4 instead!
case DataType::F16:
case DataType::F32:
compare_result = fmt::format("vec4({})", compare_result);
break;
default:
LOG_ERROR("Unexpected data type to convert to compatible type from VTSTMSK!");
break;
}
// These can't be stored to Predicate registers obiviously
variables.store(decoded_inst.opr.dest, compare_result, 0b1111, 0);
return true;
}
+510
View File
@@ -0,0 +1,510 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/glsl/code_writer.h>
#include <shader/glsl/params.h>
#include <shader/glsl/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
using namespace glsl;
bool USSETranslatorVisitorGLSL::vmov(
ExtPredicate pred,
bool skipinv,
Imm1 test_bit_2,
Imm1 src0_comp_sel,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end_or_src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
MoveType move_type,
RepeatCount repeat_count,
bool nosched,
DataType move_data_type,
Imm1 test_bit_1,
Imm4 src0_swiz,
Imm1 src0_bank_sel,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm4 dest_mask,
Imm6 dest_n,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) {
if (!USSETranslatorVisitor::vmov(pred, skipinv, test_bit_2, src0_comp_sel, syncstart, dest_bank_ext, end_or_src0_bank_ext,
src1_bank_ext, src2_bank_ext, move_type, repeat_count, nosched, move_data_type, test_bit_1, src0_swiz,
src0_bank_sel, dest_bank_sel, src1_bank_sel, src2_bank_sel, dest_mask, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
const bool is_conditional = (move_type != MoveType::UNCONDITIONAL);
const bool is_u8_conditional = decoded_inst.opcode == Opcode::VMOVCU8;
const std::size_t comp_count = dest_mask_to_comp_count(decoded_dest_mask);
std::string compare_func;
if (is_conditional) {
CompareMethod compare_method = static_cast<CompareMethod>((test_bit_2 << 1) | test_bit_1);
switch (compare_method) {
case CompareMethod::LT_ZERO:
compare_func = ((comp_count > 1) ? "lessThan" : "<");
break;
case CompareMethod::LTE_ZERO:
compare_func = ((comp_count > 1) ? "lessThanEqual" : "<=");
break;
case CompareMethod::NE_ZERO:
compare_func = ((comp_count > 1) ? "notEqual" : "!=");
break;
case CompareMethod::EQ_ZERO:
compare_func = ((comp_count > 1) ? "equal" : "<");
break;
}
}
if ((move_data_type == DataType::F16) || (move_data_type == DataType::F32)) {
set_repeat_multiplier(2, 2, 2, 2);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
std::string source_1 = variables.load(decoded_inst.opr.src1, decoded_dest_mask, src1_repeat_offset);
std::string result;
if (source_1.empty()) {
LOG_ERROR("Source not Loaded");
return false;
}
if (is_conditional) {
std::string source_to_compare_with_0 = variables.load(decoded_inst.opr.src0, decoded_dest_mask, src0_repeat_offset);
std::string source_2 = variables.load(decoded_inst.opr.src2, decoded_dest_mask, src2_repeat_offset);
if (comp_count == 1) {
result = fmt::format("{} {} 0 ? {} : {}", source_to_compare_with_0, compare_func, source_1, source_2);
} else {
if (is_float_data_type(decoded_inst.opr.src1.type) || m_features.support_glsl_mixing_integers) {
result = fmt::format("mix({}, {}, {}({}, {}vec{}(0)))", source_2, source_1, compare_func, source_to_compare_with_0,
is_u8_conditional ? "u" : "", comp_count);
} else {
char prefix = (is_unsigned_integer_data_type(decoded_inst.opr.src1.type)) ? 'u' : 'i';
result = fmt::format("{} * (1{} - {}vec{}({}({}, {}vec{}(0)))) + {} * {}vec{}({}({}, {}vec{}(0)))",
source_2, is_unsigned_integer_data_type(decoded_inst.opr.src1.type) ? "u" : "",
prefix, comp_count, compare_func, source_to_compare_with_0, prefix, comp_count,
source_1, prefix, comp_count, compare_func, source_to_compare_with_0, prefix, comp_count);
}
}
} else {
result = source_1;
}
variables.store(decoded_inst.opr.dest, result, decoded_dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorGLSL::vpck(
ExtPredicate pred,
bool skipinv,
bool nosched,
Imm1 unknown,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
RepeatCount repeat_count,
Imm3 src_fmt,
Imm3 dest_fmt,
Imm4 dest_mask,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm7 dest_n,
Imm2 comp_sel_3,
Imm1 scale,
Imm2 comp_sel_1,
Imm2 comp_sel_2,
Imm6 src1_n,
Imm1 comp0_sel_bit1,
Imm6 src2_n,
Imm1 comp_sel_0_bit0) {
if (!USSETranslatorVisitor::vpck(pred, skipinv, nosched, unknown, syncstart, dest_bank_ext,
end, src1_bank_ext, src2_bank_ext, repeat_count, src_fmt, dest_fmt, dest_mask, dest_bank_sel,
src1_bank_sel, src2_bank_sel, dest_n, comp_sel_3, scale, comp_sel_1, comp_sel_2, src1_n,
comp0_sel_bit1, src2_n, comp_sel_0_bit0)) {
return false;
}
// Doing this extra dest type check for future change in case I'm wrong (pent0)
if (is_integer_data_type(decoded_inst.opr.dest.type)) {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
} else {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
std::string source = variables.load(decoded_inst.opr.src1, dest_mask, src1_repeat_offset);
if (source.empty()) {
LOG_ERROR("Source not loaded");
return false;
}
std::string vec_prefix_str = is_unsigned_integer_data_type(decoded_inst.opr.src1.type) ? "u" : (is_signed_integer_data_type(decoded_inst.opr.src1.type) ? "i" : "");
const std::size_t comp_count = dest_mask_to_comp_count(dest_mask);
if (decoded_inst.opr.src2.type != DataType::UNK) {
Operand copy_src1 = decoded_inst.opr.src1;
Operand copy_src2 = decoded_inst.opr.src2;
copy_src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
copy_src2.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
std::string source1 = variables.load(copy_src1, 0b11, src1_repeat_offset);
std::string source2 = variables.load(copy_src2, 0b11, src2_repeat_offset);
std::string swizz_str;
for (std::uint32_t i = 0; i < 4; i++) {
if (dest_mask & (1 << i)) {
swizz_str += static_cast<char>('w' + ((static_cast<int>(decoded_inst.opr.src1.swizzle[i]) - static_cast<int>(SwizzleChannel::C_X) + 1) % 4));
}
}
source = fmt::format("{}vec4({}, {}).{}", vec_prefix_str, source1, source2, swizz_str);
}
if (!is_float_data_type(decoded_inst.opr.dest.type) && is_float_data_type(decoded_inst.opr.src1.type)) {
std::string dest_dt_type = is_unsigned_integer_data_type(decoded_inst.opr.src1.type) ? "uint" : "int";
if (scale) {
if (comp_count == 1) {
switch (decoded_inst.opr.dest.type) {
case DataType::UINT8:
source = fmt::format("{}({} * 255.0)", dest_dt_type, source);
break;
case DataType::UINT16:
source = fmt::format("{}({} * 65535.0)", dest_dt_type, source);
break;
case DataType::UINT32:
source = fmt::format("{}({} * 4294967295.0)", dest_dt_type, source);
break;
case DataType::INT8:
source = fmt::format("(({} < 0) ? {} * 128.0 : {} * 127.0)", dest_dt_type, source);
break;
case DataType::INT16:
source = fmt::format("(({} < 0) ? {} * 32768.0 : {} * 32767.0)", dest_dt_type, source);
break;
case DataType::INT32:
source = fmt::format("(({} < 0) ? {} * 2147483648.0 : {} * 2147483647.0)", dest_dt_type, source);
break;
default:
break;
}
} else {
switch (decoded_inst.opr.dest.type) {
case DataType::UINT8:
source = fmt::format("{}vec{}({} * vec{}(255.0))", dest_dt_type[0], comp_count, source, comp_count);
break;
case DataType::UINT16:
source = fmt::format("{}vec{}({} * vec{}(65535.0))", dest_dt_type[0], comp_count, source, comp_count);
break;
case DataType::UINT32:
source = fmt::format("{}vec{}({} * vec{}(4294967295.0))", dest_dt_type[0], comp_count, source, comp_count);
break;
case DataType::INT8:
source = fmt::format("mix({}vec{}({} * 128.0), {}vec{}({} * 127.0), greaterThanEqual({}, vec{}(0.0)))", dest_dt_type[0],
comp_count, source, dest_dt_type[0], comp_count, source, source, comp_count);
break;
case DataType::INT16:
source = fmt::format("mix({}vec{}({} * 32768.0), {}vec{}({} * 32767.0), greaterThanEqual({}, vec{}(0.0)))", dest_dt_type[0],
comp_count, source, dest_dt_type[0], comp_count, source, source, comp_count);
break;
case DataType::INT32:
source = fmt::format("mix({}vec{}({} * 2147483648.0), {}vec{}({} * 2147483647.0), greaterThanEqual({}, vec{}(0.0)))", dest_dt_type[0],
comp_count, source, dest_dt_type[0], comp_count, source, source, comp_count);
break;
default:
break;
}
}
} else {
if (comp_count == 1) {
source = dest_dt_type + "(" + source + ")";
} else {
source = fmt::format("{}vec{}({})", dest_dt_type[0], comp_count, source);
}
}
}
// source is int destination is float
if (is_float_data_type(decoded_inst.opr.dest.type) && !is_float_data_type(decoded_inst.opr.src1.type)) {
std::string dest_dt_type_str = "float";
std::string dest_dt_type_str_prefix = "";
if (scale) {
if (comp_count == 1) {
switch (decoded_inst.opr.src1.type) {
case DataType::UINT8:
source = fmt::format("{}({}) / 255.0", dest_dt_type_str, source);
break;
case DataType::UINT16:
source = fmt::format("{}({}) / 65535.0", dest_dt_type_str, source);
break;
case DataType::UINT32:
source = fmt::format("{}({}) / 4294967295.0", dest_dt_type_str, source);
break;
case DataType::INT8:
source = fmt::format("(({} < 0) ? {}({}) / 128.0, {}({}) / 127.0)", source, dest_dt_type_str, source, dest_dt_type_str, source);
break;
case DataType::INT16:
source = fmt::format("(({} < 0) ? {}({}) / 32768.0, {}({}) / 32767.0)", source, dest_dt_type_str, source, dest_dt_type_str, source);
break;
case DataType::INT32:
source = fmt::format("(({} < 0) ? {}({}) / 2147483648.0, {}({}) / 2147483647.0)", source, dest_dt_type_str, source, dest_dt_type_str, source);
break;
default:
break;
}
} else {
switch (decoded_inst.opr.src1.type) {
case DataType::UINT8:
source = fmt::format("{}vec{}({}) / 255.0", dest_dt_type_str_prefix, comp_count, source);
break;
case DataType::UINT16:
source = fmt::format("{}vec{}({}) / 65535.0", dest_dt_type_str_prefix, comp_count, source);
break;
case DataType::UINT32:
source = fmt::format("{}vec{}({}) / 4294967295.0", dest_dt_type_str_prefix, comp_count, source);
break;
case DataType::INT8:
source = fmt::format("mix({}vec{}({}) / 128.0, {}vec{}({}) / 127.0, greaterThanEqual({}, {}vec{}(0.0)))", dest_dt_type_str_prefix,
comp_count, source, dest_dt_type_str_prefix, comp_count, source, source, vec_prefix_str, comp_count);
break;
case DataType::INT16:
source = fmt::format("mix({}vec{}({}) / 32768.0, {}vec{}({}) / 32767.0, greaterThanEqual({}, {}vec{}(0.0)))", dest_dt_type_str_prefix,
comp_count, source, dest_dt_type_str_prefix, comp_count, source, source, vec_prefix_str, comp_count);
break;
case DataType::INT32:
source = fmt::format("mix({}vec{}({}) / 2147483648.0, {}vec{}({}) / 2147483647.0, greaterThanEqual({}, {}vec{}(0.0)))", dest_dt_type_str_prefix,
comp_count, source, dest_dt_type_str_prefix, comp_count, source, source, vec_prefix_str, comp_count);
break;
default:
break;
}
}
} else {
if (comp_count == 1) {
source = dest_dt_type_str + "(" + source + ")";
} else {
source = fmt::format("{}vec{}({})", dest_dt_type_str_prefix, comp_count, source);
}
}
}
variables.store(decoded_inst.opr.dest, source, dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorGLSL::vldst(
Imm2 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 moe_expand,
Imm1 sync_start,
Imm1 cache_ext,
Imm1 src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 mask_count,
Imm2 addr_mode,
Imm2 mode,
Imm1 dest_bank_primattr,
Imm1 range_enable,
Imm2 data_type,
Imm1 increment_or_decrement,
Imm1 src0_bank,
Imm1 cache_by_pass12,
Imm1 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vldst(op1, pred, skipinv, nosched, moe_expand, sync_start, cache_ext, src0_bank_ext,
src1_bank_ext, src2_bank_ext, mask_count, addr_mode, mode, dest_bank_primattr, range_enable, data_type,
increment_or_decrement, src0_bank, cache_by_pass12, drc_sel, src1_bank, src2_bank, dest_n, src0_n, src1_n,
src2_n)) {
return false;
}
// TODO:
// - Store instruction
// - Post or pre or any increment mode.
DataType type_to_ldst = DataType::UNK;
switch (data_type) {
case 0:
type_to_ldst = DataType::F32;
break;
case 1:
type_to_ldst = DataType::F16;
break;
case 2:
type_to_ldst = DataType::C10;
break;
default:
break;
}
const int total_number_to_fetch = mask_count + 1;
const int total_bytes_fo_fetch = get_data_type_size(type_to_ldst) * total_number_to_fetch;
Operand to_store;
if (is_translating_secondary_program()) {
to_store.bank = RegisterBank::SECATTR;
} else {
if (dest_bank_primattr) {
to_store.bank = RegisterBank::PRIMATTR;
} else {
to_store.bank = RegisterBank::TEMP;
}
}
to_store.num = dest_n;
to_store.type = DataType::F32;
// TODO: is source_2 in word or byte? Is it even used at all?
std::string source_0 = variables.load(decoded_inst.opr.src0, 0b1, 0);
if (decoded_inst.opr.src1.bank == RegisterBank::IMMEDIATE) {
decoded_inst.opr.src1.num *= get_data_type_size(type_to_ldst);
}
std::string source_1 = variables.load(decoded_inst.opr.src1, 0b1, 0);
std::string source_2 = variables.load(decoded_inst.opr.src2, 0b1, 0);
// Seems that if it's indexed by register, offset is in bytes and based on 0x10000?
// Maybe that's just how the memory map operates. I'm not sure. However the literals on all shader so far is that
// Another thing is that, when moe expand is not enable, there seems to be 4 bytes added before fetching... No absolute prove.
// Maybe moe expand means it's not fetching after all? Dunno
std::uint32_t REG_INDEX_BASE = 0x10000;
if (decoded_inst.opr.src1.bank != shader::usse::RegisterBank::IMMEDIATE) {
source_1 = fmt::format("{} - {}", source_1, REG_INDEX_BASE);
}
std::string base = fmt::format("{} + {} + {}", source_0, source_1, source_2);
if (!moe_expand) {
base = base + " + 4";
}
// Address sometimes can be unaligned (start at mod 2 instead, as far as sunho wrote here)
// If not we can just optimize it to load a vec4 at a time , haha
writer.add_to_current_body(std::string("base_temp = ") + base + ";");
for (int i = 0; i < total_bytes_fo_fetch / 4; ++i) {
variables.store(to_store, "fetchMemory(base_temp)", 0b1, 0);
to_store.num += 1;
writer.add_to_current_body("base_temp += 4;");
}
program_state.should_generate_vld_func = true;
return true;
}
bool USSETranslatorVisitorGLSL::limm(
bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
Imm6 imm_value_bits26to31,
ExtPredicate pred,
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits) {
decoded_inst.opcode = Opcode::MOV;
std::uint32_t imm_value = imm_value_first_21bits | (imm_value_bits21to25 << 21) | (imm_value_bits26to31 << 26);
decoded_inst.dest_mask = 0b1;
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_num, dest_bank, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest.type = DataType::UINT32;
const std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(decoded_inst.opcode));
LOG_DISASM("{} {} #0x{:X}", disasm_str, disasm::operand_to_str(decoded_inst.opr.dest, 0b1, 0), imm_value);
variables.store(decoded_inst.opr.dest, fmt::format("0x{:X}", imm_value), 0b1, 0);
return true;
}
+369
View File
@@ -0,0 +1,369 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/glsl/params.h>
#include <shader/glsl/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
using namespace glsl;
bool USSETranslatorVisitorGLSL::vbw(
Imm3 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
bool repeat_sel,
Imm1 sync_start,
Imm1 dest_ext,
Imm1 end,
Imm1 src1_ext,
Imm1 src2_ext,
RepeatCount repeat_count,
Imm1 src2_invert,
Imm5 src2_rot,
Imm2 src2_exth,
Imm1 op2,
Imm1 bitwise_partial,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src2_sel,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vbw(op1, pred, skipinv, nosched, repeat_sel, sync_start, dest_ext, end, src1_ext, src2_ext,
repeat_count, src2_invert, src2_rot, src2_exth, op2, bitwise_partial, dest_bank, src1_bank, src2_bank,
dest_n, src2_sel, src1_n, src2_n)) {
return false;
}
set_repeat_multiplier(1, 1, 1, 1);
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
bool immediate = src2_ext && decoded_inst.opr.src2.bank == RegisterBank::IMMEDIATE;
uint32_t value = 0;
if (src2_rot) {
LOG_WARN("Bitwise Rotations are unsupported.");
return false;
}
std::uint32_t src2_constant = 0;
if (immediate) {
src2_constant = src2_n | (static_cast<uint32_t>(src2_sel) << 7) | (static_cast<uint32_t>(src2_exth) << 14);
if (src2_invert) {
src2_constant = ~src2_constant;
}
}
std::string operator_str;
switch (decoded_inst.opcode) {
case Opcode::OR: operator_str = "|"; break;
case Opcode::AND: operator_str = "&"; break;
case Opcode::XOR: operator_str = "^"; break;
case Opcode::ROL:
LOG_WARN("Bitwise Rotate Left operation unsupported.");
return false; // TODO: SPIRV doesn't seem to have a rotate left operation!
case Opcode::ASR: operator_str = ">>"; break;
case Opcode::SHL: operator_str = "<<"; break;
case Opcode::SHR: operator_str = ">>"; break;
default: return false;
}
std::string calc_result;
// optimisation. (any OR 0 || any XOR 0 || any AND 0xFFFFFFFF) -> assign
if ((decoded_inst.opcode == Opcode::OR || decoded_inst.opcode == Opcode::XOR) && immediate && src2_constant == 0
|| ((decoded_inst.opcode == Opcode::AND) && immediate && src2_constant == std::numeric_limits<decltype(src2_constant)>::max())) {
if (decoded_inst.opr.dest.is_same(decoded_inst.opr.src1, 0b0001)) {
// Do nothing actually
return true;
}
decoded_inst.opr.src1.type = DataType::F32;
decoded_inst.opr.dest.type = DataType::F32;
calc_result = variables.load(decoded_inst.opr.src1, 0b0001, src1_repeat_offset);
} else {
std::string src1 = variables.load(decoded_inst.opr.src1, 0b0001, src1_repeat_offset);
std::string src2 = immediate ? fmt::format("{}", src2_constant) : variables.load(decoded_inst.opr.src2, 0b0001, src2_repeat_offset);
if (src1.empty() || src2.empty()) {
LOG_ERROR("Source not loaded");
return false;
}
calc_result = fmt::format("{} {} {}", src1, operator_str, src2);
}
if (calc_result.empty()) {
LOG_ERROR("Source not loaded");
return false;
}
variables.store(decoded_inst.opr.dest, calc_result, 0b0001, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorGLSL::i8mad(
Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 csel0,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 repeat_count,
Imm1 saturated,
Imm1 cmod0,
Imm1 asel0,
Imm1 amod2,
Imm1 amod1,
Imm1 amod0,
Imm1 csel1,
Imm1 csel2,
Imm1 src0_neg,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_num,
Imm7 src0_num,
Imm7 src1_num,
Imm7 src2_num) {
if (!USSETranslatorVisitor::i8mad(pred, cmod1, skipinv, nosched, csel0, dest_bank_ext, end, src1_bank_ext,
src2_bank_ext, cmod2, repeat_count, saturated, cmod0, asel0, amod2, amod1, amod0, csel1, csel2, src0_neg,
src0_bank, dest_bank, src1_bank, src2_bank, dest_num, src0_num, src1_num, src2_num)) {
return false;
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
std::string src1_mul = variables.load(decoded_inst.opr.src1, 0b1111, src1_repeat_offset);
std::string src2_mul = variables.load(decoded_inst.opr.src2, 0b1111, src2_repeat_offset);
std::string src0_add = variables.load(decoded_inst.opr.src0, 0b1111, src0_repeat_offset);
std::string final_add = src0_add;
usse::Swizzle3 add_swizz_rgb = SWIZZLE_CHANNEL_3_DEFAULT;
bool add_swizz_rgb_src0 = true;
if ((csel0 != 0) || (asel0 != 0)) {
switch (csel0) {
case 0:
final_add = src0_add + ".xyz";
break;
case 1:
// Use src1 rgb
final_add = src1_mul + ".xyz";
break;
case 2:
// Use src0 full alpha
final_add = src0_add + ".www";
break;
case 3:
// Use src1 full alpha
final_add = src1_mul + ".www";
break;
default:
assert(false);
break;
}
switch (asel0) {
case 0:
// Use src0 alpha
final_add += std::string(", ") + src0_add + ".w";
break;
case 1:
// Use src1 alpha
final_add += std::string(", ") + src1_mul + ".w";
break;
default:
assert(false);
break;
}
final_add = std::string("uvec4(") + final_add + ")";
}
// For some reason there's no FMA for integer
std::string result = fmt::format("{} * {} {} {}", src1_mul, src2_mul, src0_neg ? "-" : "+", final_add);
variables.store(decoded_inst.opr.dest, result, 0b1111, dest_repeat_offset);
decoded_inst.opr.src0.swizzle[0] = add_swizz_rgb[0];
decoded_inst.opr.src0.swizzle[1] = add_swizz_rgb[1];
decoded_inst.opr.src0.swizzle[2] = add_swizz_rgb[2];
END_REPEAT();
return true;
}
bool USSETranslatorVisitorGLSL::i16mad(
ShortPredicate pred,
Imm1 abs,
Imm1 skipinv,
Imm1 nosched,
Imm1 src2_neg,
Imm1 sel1h_upper8,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 repeat_count,
Imm2 mode,
Imm2 src2_format,
Imm2 src1_format,
Imm1 sel2h_upper8,
Imm2 or_shift,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i16mad(pred, abs, skipinv, nosched, src2_neg, sel1h_upper8, dest_bank_ext, end,
src1_bank_ext, src2_bank_ext, repeat_count, mode, src2_format, src1_format, sel2h_upper8, or_shift,
src0_bank, dest_bank, src1_bank, src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
std::string source0 = variables.load(decoded_inst.opr.src0, 0b11, src0_repeat_offset);
std::string source1 = variables.load(decoded_inst.opr.src1, decoded_source_mask, src1_repeat_offset);
std::string source2 = variables.load(decoded_inst.opr.src2, decoded_source_mask_2, src2_repeat_offset);
bool is_signed = (mode >= 2);
source1 = fmt::format("{}vec2({})", is_signed ? "i" : "u", source1);
source2 = fmt::format("{}vec2({})", is_signed ? "i" : "u", source2);
std::string result = fmt::format("{} * {} + {}", source0, source1, source2);
variables.store(decoded_inst.opr.dest, result, 0b11, dest_repeat_offset);
END_REPEAT();
return true;
}
bool USSETranslatorVisitorGLSL::i32mad(
ShortPredicate pred,
Imm1 src0_high,
Imm1 nosched,
Imm1 src1_high,
Imm1 src2_high,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
RepeatCount repeat_count,
bool is_signed,
bool is_sat,
Imm2 src2_type,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i32mad(pred, src0_high, nosched, src1_high, src2_high, dest_bank_ext, end,
src1_bank_ext, src2_bank_ext, repeat_count, is_signed, is_sat, src2_type, src0_bank,
dest_bank, src1_bank, src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
std::string vsrc0 = variables.load(decoded_inst.opr.src0, decoded_source_mask, 0);
std::string vsrc1 = variables.load(decoded_inst.opr.src1, decoded_source_mask_2, 0);
std::string vsrc2 = variables.load(decoded_inst.opr.src2, decoded_source_mask_3, 0);
std::string result = fmt::format("{} * {} + {}", vsrc0, vsrc1, vsrc2);
variables.store(decoded_inst.opr.dest, result, 0b1, 0);
return true;
}
bool USSETranslatorVisitorGLSL::i32mad2(
ExtPredicate pred,
Imm1 nosched,
Imm2 sn,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
bool src0_bank_ext,
Imm3 count,
bool is_signed,
Imm1 negative_src1,
Imm1 negative_src2,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i32mad2(pred, nosched, sn, dest_bank_ext, end, src1_bank_ext, src2_bank_ext,
src0_bank_ext, count, is_signed, negative_src1, negative_src2, src0_bank, dest_bank, src1_bank,
src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
std::string vsrc0 = variables.load(decoded_inst.opr.src0, 0b1, 0);
std::string vsrc1 = variables.load(decoded_inst.opr.src1, 0b1, 0);
std::string vsrc2 = variables.load(decoded_inst.opr.src2, 0b1, 0);
std::string result = fmt::format("{} * {} + {}", vsrc0, vsrc1, vsrc2);
// sn is mysterious argument.
// These are confirmed by hw testing:
// - pa = x * y + z (sn = 0) => pa = x * y + z
// - i = x * y + z (sn = 0) and then pa = x * y + i (sn = 1) => pa = x * y + z
// - pa = x * y + z (sn = 1) => crash
// TODO: properly implement this when we get more powerful fuzzer that can handle fpinternal.
if (sn == 0) {
variables.store(decoded_inst.opr.dest, result, 0b1, 0);
} else {
variables.store(decoded_inst.opr.dest, vsrc2, 0b1, 0);
}
return true;
}
@@ -0,0 +1,33 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/disasm.h>
#include <shader/glsl/code_writer.h>
#include <shader/glsl/translator.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
using namespace glsl;
bool USSETranslatorVisitorGLSL::kill(
ShortPredicate pred) {
LOG_DISASM("{:016x}: KILL {}", m_instr, disasm::s_predicate_str(pred));
writer.add_to_current_body("discard;");
return true;
}
@@ -0,0 +1,254 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/disasm.h>
#include <shader/glsl/code_writer.h>
#include <shader/glsl/params.h>
#include <shader/glsl/translator.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
using namespace glsl;
std::string USSETranslatorVisitorGLSL::do_fetch_texture(const std::string tex, std::string coord_name, const DataType dest_type, const int lod_mode,
const std::string extra1, const std::string extra2) {
std::string result;
switch (lod_mode) {
case 4:
result = fmt::format("textureProj({}, {})", tex, coord_name);
break;
case 5:
result = fmt::format("textureProjCube({}, {})", tex, coord_name);
break;
case 0:
case 1:
result = fmt::format("texture({}, {})", tex, coord_name);
break;
case 2:
result = fmt::format("textureLod({}, {}, {})", tex, coord_name, extra1);
break;
case 3:
result = fmt::format("textureGrad({}, {}, {}, {})", tex, coord_name, extra1, extra2);
break;
default:
return "";
}
if (dest_type == DataType::UINT8) {
result = fmt::format("uvec4({} * 255.0)", result);
}
return result;
}
void USSETranslatorVisitorGLSL::do_texture_queries(const NonDependentTextureQueries &texture_queries) {
Operand store_op;
store_op.bank = RegisterBank::PRIMATTR;
store_op.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
store_op.type = DataType::F32;
for (auto &texture_query : texture_queries) {
bool proj = (texture_query.proj_pos >= 0);
std::string coord_access = fmt::format("v_TexCoord{}", texture_query.coord_index);
std::string proj_access = coord_access + ".";
if (texture_query.sampler_cube) {
coord_access += ".xyz";
} else {
coord_access += ".xy";
}
if (proj) {
coord_access += static_cast<char>('w' + (texture_query.proj_pos + 1) % 4);
}
std::string fetch_result = do_fetch_texture(texture_query.sampler_name, coord_access, static_cast<DataType>(texture_query.data_type),
proj ? (texture_query.sampler_cube ? 5 : 4) : 0, "", "");
store_op.num = texture_query.offset_in_pa;
store_op.type = static_cast<DataType>(texture_query.data_type);
if (static_cast<DataType>(texture_query.data_type) == DataType::UNK) {
// Manual check
writer.add_to_current_body(fmt::format("temp4 = {};", fetch_result));
// S8
writer.add_to_current_body(fmt::format("if (renderFragInfo.integral_query_formats[{}][{}] > {}) {{", texture_query.sampler_index / 4,
texture_query.sampler_index % 4, INTEGRAL_TEX_QUERY_TYPE_8BIT_SIGNED));
writer.indent_current_body();
store_op.type = DataType::INT8;
variables.store(store_op, "ivec4(temp4 * 128.0)", 0b1111, 0, true);
writer.dedent_current_body();
// U8
writer.add_to_current_body(fmt::format("}} else if (renderFragInfo.integral_query_formats[{}][{}] > {}) {{", texture_query.sampler_index / 4,
texture_query.sampler_index % 4, INTEGRAL_TEX_QUERY_TYPE_8BIT_UNSIGNED));
writer.indent_current_body();
store_op.type = DataType::UINT8;
variables.store(store_op, "uvec4(temp4 * 255.0)", 0b1111, 0, true);
writer.dedent_current_body();
// F16
writer.add_to_current_body(fmt::format("}} else if (renderFragInfo.integral_query_formats[{}][{}] > {}) {{", texture_query.sampler_index / 4,
texture_query.sampler_index % 4, INTEGRAL_TEX_QUERY_TYPE_16BIT));
writer.indent_current_body();
store_op.type = DataType::F16;
variables.store(store_op, "temp4", 0b1111, 0, true);
writer.dedent_current_body();
// 32-bit
writer.add_to_current_body(fmt::format("}} else {{", texture_query.sampler_index / 4, texture_query.sampler_index % 4));
writer.indent_current_body();
store_op.type = DataType::F32;
variables.store(store_op, "temp4", 0b1111, 0, true);
writer.dedent_current_body();
writer.add_to_current_body("}");
} else {
variables.store(store_op, fetch_result, 0b1111, 0, true);
}
}
}
bool USSETranslatorVisitorGLSL::smp(
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 syncstart,
Imm1 minpack,
Imm1 src0_ext,
Imm1 src1_ext,
Imm1 src2_ext,
Imm2 fconv_type,
Imm2 mask_count,
Imm2 dim,
Imm2 lod_mode,
bool dest_use_pa,
Imm2 sb_mode,
Imm2 src0_type,
Imm1 src0_bank,
Imm2 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::smp(pred, skipinv, nosched, syncstart, minpack, src0_ext, src2_ext, src2_ext, fconv_type,
mask_count, dim, lod_mode, dest_use_pa, sb_mode, src0_type, src0_bank, drc_sel, src1_bank, src2_bank,
dest_n, src0_n, src1_n, src2_n)) {
return false;
}
// LOD mode: none, bias, replace, gradient
if ((lod_mode != 0) && (lod_mode != 2) && (lod_mode != 3)) {
LOG_ERROR("Sampler LOD replace not implemented!");
return true;
}
// Base 0, turn it to base 1
dim += 1;
std::uint32_t coord_mask = 0b0011;
if (dim == 3) {
coord_mask = 0b0111;
} else if (dim == 1) {
coord_mask = 0b0001;
}
// Generate simple stuff
// Load the coord
std::string coord = variables.load(decoded_inst.opr.src0, coord_mask, 0);
if (coord.empty()) {
LOG_ERROR("Coord not loaded");
return false;
}
if (dim == 1) {
// It should be a line, so Y should be zero. There are only two dimensions texture, so this is a guess (seems concise)
coord = fmt::format("vec2({}, 0)", coord);
}
std::string sampler_name;
if (program_state.samplers_on_offset.count(decoded_inst.opr.src1.num)) {
sampler_name = program_state.samplers_on_offset.at(decoded_inst.opr.src1.num).first;
} else {
LOG_ERROR("Can't get the sampler (sampler doesn't exist!)");
return true;
}
// Either LOD number or gradient number
std::string extra1;
std::string extra2;
if (lod_mode != 0) {
switch (lod_mode) {
case 2:
extra1 = variables.load(decoded_inst.opr.src2, 0b1, 0);
break;
case 3:
if (dim == 3) {
extra1 = variables.load(decoded_inst.opr.src2, 0b111, 0);
} else {
extra1 = variables.load(decoded_inst.opr.src2, 0b11, 0);
}
// Skip to next PA/INTERNAL to get DDY. Observed on shader in game like Hatsune Miku Diva X
if (dim == 3) {
extra2 = variables.load(decoded_inst.opr.src2, 0b111, 4);
} else {
extra2 = variables.load(decoded_inst.opr.src2, 0b11, 2);
}
break;
default:
break;
}
}
std::string result = do_fetch_texture(sampler_name, coord, DataType::F32, lod_mode, extra1, extra2);
switch (sb_mode) {
case 0:
case 1:
variables.store(decoded_inst.opr.dest, result, 0b1111, 0);
break;
case 3: {
// TODO: figure out what to fill here
// store(inst.opr.dest, stub, 0b1111);
variables.store(decoded_inst.opr.dest, result, 0b1111, 0);
break;
}
default: {
LOG_ERROR("Unsupported sb_mode: {}", sb_mode);
}
}
return true;
}
+226 -1
View File
@@ -17,7 +17,7 @@
#include <gxm/functions.h>
#include <shader/gxp_parser.h>
#include <shader/usse_program_analyzer.h>
#include <shader/program_analyzer.h>
#include <util/align.h>
#include <util/log.h>
@@ -363,6 +363,231 @@ ProgramInput shader::get_program_input(const SceGxmProgram &program) {
program_input.inputs.push_back(item);
}
} else {
// Parse fragment input
const SceGxmProgramAttributeDescriptor *descriptor = reinterpret_cast<const SceGxmProgramAttributeDescriptor *>(
reinterpret_cast<const std::uint8_t *>(&vertex_varyings_ptr->vertex_outputs1) + vertex_varyings_ptr->vertex_outputs1);
static const std::unordered_map<std::uint32_t, std::pair<std::string, std::uint32_t>> name_map = {
{ 0xD000, { "v_Position", 0 } },
{ 0xC000, { "v_Fog", 3 } },
{ 0xA000, { "v_Color0", 1 } },
{ 0xB000, { "v_Color1", 2 } },
{ 0x0, { "v_TexCoord0", 4 } },
{ 0x1000, { "v_TexCoord1", 5 } },
{ 0x2000, { "v_TexCoord2", 6 } },
{ 0x3000, { "v_TexCoord3", 7 } },
{ 0x4000, { "v_TexCoord4", 8 } },
{ 0x5000, { "v_TexCoord5", 9 } },
{ 0x6000, { "v_TexCoord6", 10 } },
{ 0x7000, { "v_TexCoord7", 11 } },
{ 0x8000, { "v_TexCoord8", 12 } },
{ 0x9000, { "v_TexCoord9", 13 } },
};
std::uint32_t pa_offset = 0;
std::uint32_t coord_added = 0;
// We first iterate, and gather all possible IN variables first.
// After that we do texture queries, since we need coord variable to be added and existed.
for (size_t i = 0; i < vertex_varyings_ptr->varyings_count; i++, descriptor++) {
// 4 bit flag indicates a PA!
if ((descriptor->attribute_info & 0x4000F000) != 0xF000) {
std::uint32_t input_id = (descriptor->attribute_info & 0x4000F000);
std::string pa_name;
std::uint32_t pa_loc = 0;
if (input_id & 0x40000000) {
pa_name = "v_SpriteCoord";
} else {
pa_name = name_map.at(input_id).first;
pa_loc = name_map.at(input_id).second;
}
DataType pa_dtype = DataType::UINT8;
uint32_t input_type = (descriptor->attribute_info & 0x30100000);
if (input_type == 0x20000000) {
pa_dtype = DataType::F16;
} else if (input_type == 0x10000000) {
pa_dtype = DataType::C10;
// TODO: Supply data type
} else if (input_type == 0x100000) {
if (input_id == 0xA000 || input_id == 0xB000) {
pa_dtype = DataType::F32;
}
} else if (input_id != 0xA000 && input_id != 0xB000) {
pa_dtype = DataType::F32;
}
// Create PA Iterator SPIR-V variable
const auto num_comp = ((descriptor->attribute_info >> 22) & 3) + 1;
// Force this to 4. TODO: Don't
// Reason is for compability between vertex and fragment. This is like an anti-crash when linking.
// Fragment will only copy what it needed.
Input item;
item.component_count = num_comp;
item.array_size = 1;
item.bank = RegisterBank::PRIMATTR;
item.generic_type = GenericType::VECTOR;
item.type = pa_dtype;
item.offset = pa_offset;
AttributeInputSource source = {};
source.semantic = (input_id == 0xD000) ? SCE_GXM_PARAMETER_SEMANTIC_NORMAL : 0;
source.name = pa_name;
source.regformat = false;
source.opt_location = pa_loc;
item.source = source;
if (input_id >= 0 && input_id <= 0x9000) {
input_id /= 0x1000;
coord_added |= (1 << input_id);
} else if (input_id == 0xD000) {
// Not sure, comment out for now
// input_id = 10;
// do_coord = true;
}
program_input.inputs.push_back(item);
pa_offset += ((descriptor->size >> 4) & 3) + 1;
}
uint32_t tex_coord_index = (descriptor->attribute_info & 0x40F);
if (tex_coord_index != 0xF) {
pa_offset += ((descriptor->size >> 6) & 3) + 1;
}
}
descriptor = reinterpret_cast<const SceGxmProgramAttributeDescriptor *>(
reinterpret_cast<const std::uint8_t *>(&vertex_varyings_ptr->vertex_outputs1) + vertex_varyings_ptr->vertex_outputs1);
pa_offset = 0;
for (size_t i = 0; i < vertex_varyings_ptr->varyings_count; i++, descriptor++) {
if ((descriptor->attribute_info & 0x4000F000) != 0xF000) {
pa_offset += ((descriptor->size >> 4) & 3) + 1;
}
uint32_t tex_coord_index = (descriptor->attribute_info & 0x40F);
uint32_t anon_tex_count = 0;
// Process texture query variable (iterator), stored on a PA (primary attribute) register
if (tex_coord_index != 0xF) {
if (tex_coord_index == 0x400) {
// Texcoord variable
tex_coord_index = 10;
}
bool anonymous = false;
bool is_cube = false;
const auto sampler = std::find_if(program_input.samplers.begin(), program_input.samplers.end(), [=](auto x) { return x.index == descriptor->resource_index; });
if (sampler == program_input.samplers.end()) {
LOG_INFO("Sample symbol stripped, using anonymous sampler");
Sampler sampler_info;
sampler_info.name = fmt::format("anonymousSampler{}", descriptor->resource_index);
sampler_info.index = descriptor->resource_index;
sampler_info.is_cube = false; // I don't know :(
program_input.samplers.push_back(std::move(sampler_info));
} else {
is_cube = sampler->is_cube;
}
const auto component_type = (descriptor->component_info >> 4) & 3;
const auto swizzle_texcoord = (descriptor->attribute_info & 0x300);
DataType store_type = DataType::F16;
switch (component_type) {
// 0 should be integral
case 0: {
store_type = DataType::UNK;
break;
}
case 1: {
// Maybe char?
LOG_WARN("Unsupported texture component: {}", component_type);
break;
}
case 2: {
store_type = DataType::F16;
break;
}
case 3: {
store_type = DataType::F32;
break;
}
default: {
LOG_WARN("Unsupported texture component: {}", component_type);
break;
}
}
int tex_coord_comp_count = 2;
int prod_pos = -1;
if (swizzle_texcoord == 0x300) {
tex_coord_comp_count = 3;
prod_pos = 2;
} else if (swizzle_texcoord == 0x200) {
// Not really sure
tex_coord_comp_count = 4;
prod_pos = 3;
}
if (is_cube) {
prod_pos = 3;
}
uint32_t num_component = 4;
if ((descriptor->component_info & 0x40) != 0x40) {
num_component = 4;
} /* else number of components = texture pixel component count. Too bad its not yet supported */
if ((coord_added & (1 << tex_coord_index)) == 0) {
Input item;
item.component_count = 4;
item.array_size = 1;
item.bank = RegisterBank::SPECIAL;
AttributeInputSource source = {};
source.name = (tex_coord_index == 10) ? "gl_PointCoord" : fmt::format("v_TexCoord{}", tex_coord_index);
source.index = tex_coord_index;
source.opt_location = tex_coord_index;
source.regformat = false;
item.source = source;
coord_added |= (1 << tex_coord_index);
program_input.inputs.push_back(std::move(item));
}
Input non_dependent_load_item;
non_dependent_load_item.array_size = 1;
non_dependent_load_item.offset = pa_offset;
non_dependent_load_item.type = store_type;
non_dependent_load_item.bank = RegisterBank::PRIMATTR;
non_dependent_load_item.component_count = num_component;
NonDependentSamplerSampleSource source;
source.coord_index = tex_coord_index;
source.coord_load_comp_count = tex_coord_comp_count;
source.sampler_index = descriptor->resource_index;
source.proj_pos = prod_pos;
non_dependent_load_item.source = source;
program_input.inputs.push_back(std::move(non_dependent_load_item));
// Size of this extra pa occupied
pa_offset += ((descriptor->size >> 6) & 3) + 1;
}
}
}
return program_input;
@@ -18,11 +18,11 @@
#include <gxm/functions.h>
#include <gxm/types.h>
#include <shader/gxp_parser.h>
#include <shader/usse_program_analyzer.h>
#include <shader/program_analyzer.h>
#include <cassert>
#include <shader/usse_types.h>
#include <shader/types.h>
namespace shader::usse {
bool is_kill(const std::uint64_t inst) {
@@ -106,9 +106,11 @@ std::uint8_t get_predicate(const std::uint64_t inst) {
return 0;
}
}
// SOP2, I32MAD
// SOP2, SOP2M, SOP3, I32MAD
case 0b10000:
case 0b10001:
case 0b10101:
case 0b10010:
case 0b10011: {
uint8_t predicate = ((inst >> 32) & ~0xF9FFFFFF) >> 25;
return static_cast<uint8_t>(short_predicate_to_ext(static_cast<ShortPredicate>(predicate)));
@@ -16,18 +16,17 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv_recompiler.h>
#include <shader/usse_disasm.h>
#include <shader/usse_program_analyzer.h>
#include <shader/usse_utilities.h>
#include <shader/disasm.h>
#include <shader/program_analyzer.h>
#include <shader/recompiler.h>
#include <shader/spirv/utilities.h>
#include <gxm/functions.h>
#include <gxm/types.h>
#include <shader/gxp_parser.h>
#include <shader/profile.h>
#include <shader/usse_translator_entry.h>
#include <shader/usse_translator_types.h>
#include <shader/usse_utilities.h>
#include <shader/spirv/translator_entry.h>
#include <shader/translator_types.h>
#include <util/fs.h>
#include <util/log.h>
#include <util/overloaded.h>
@@ -987,6 +986,9 @@ static SpirvShaderParameters create_parameters(spv::Builder &b, const SceGxmProg
[&](const AttributeInputSource &s) {
add_var_to_reg(input, s.name, s.semantic, true, s.regformat, in_fcount_allocated / 4);
in_fcount_allocated += ((input.array_size * input.component_count + 3) / 4 * 4);
},
[&](const NonDependentSamplerSampleSource &s) {
// TODO!
} },
input.source);
}
@@ -1403,6 +1405,8 @@ static void generate_update_mask_body(spv::Builder &b, utils::SpirvUtilFunctions
b.createStore(mask_v, out);
}
void spirv_disasm_print(const usse::SpirvCode &spirv_binary, std::string *spirv_dump = nullptr);
static SpirvCode convert_gxp_to_spirv_impl(const SceGxmProgram &program, const std::string &shader_hash, const FeatureState &features, TranslationState &translation_state, const std::vector<SceGxmVertexAttribute> *hint_attributes, bool force_shader_debug, std::function<bool(const std::string &ext, const std::string &dump)> dumper) {
SpirvCode spirv;
@@ -1603,6 +1607,7 @@ usse::SpirvCode convert_gxp_to_spirv(const SceGxmProgram &program, const std::st
return convert_gxp_to_spirv_impl(program, shader_name, features, translation_state, hint_attributes, force_shader_debug, dumper);
}
/*
std::string convert_gxp_to_glsl(const SceGxmProgram &program, const std::string &shader_name, const FeatureState &features, const std::vector<SceGxmVertexAttribute> *hint_attributes, bool maskupdate, bool force_shader_debug, std::function<bool(const std::string &ext, const std::string &dump)> dumper) {
TranslationState translation_state;
translation_state.is_fragment = program.is_fragment();
@@ -1626,7 +1631,7 @@ std::string convert_gxp_to_glsl(const SceGxmProgram &program, const std::string
}
return source;
}
}*/
void convert_gxp_to_glsl_from_filepath(const std::string &shader_filepath) {
const fs::path shader_filepath_str{ shader_filepath };
+214
View File
@@ -0,0 +1,214 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv/translator.h>
#include <fmt/format.h>
#include <gxm/types.h>
#include <shader/disasm.h>
#include <shader/translator_types.h>
#include <util/log.h>
#include <util/optional.h>
#include <map>
namespace shader::usse {
//
// Decoder/translator usage
//
USSERecompilerSpirv::USSERecompilerSpirv(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features, const SpirvShaderParameters &parameters,
utils::SpirvUtilFunctions &utils, const NonDependentTextureQueryCallInfos &queries, const spv::Id render_info_id)
: USSERecompiler(program)
, b(b) {
visitor = std::make_unique<USSETranslatorVisitorSpirv>(b, *this, program, features, utils, cur_instr, parameters, queries, render_info_id, false);
}
USSETranslatorVisitorSpirv *USSERecompilerSpirv::get_spirv_translator_visitor() {
return reinterpret_cast<USSETranslatorVisitorSpirv *>(visitor.get());
}
spv::Id USSERecompilerSpirv::get_condition_value(const std::uint8_t pred, const bool neg) {
const ExtPredicate predicator = static_cast<ExtPredicate>(pred);
Operand pred_opr{};
pred_opr.bank = RegisterBank::PREDICATE;
bool do_neg = neg;
if (predicator >= ExtPredicate::P0 && predicator <= ExtPredicate::P3) {
pred_opr.num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::P0);
} else if (predicator >= ExtPredicate::NEGP0 && predicator <= ExtPredicate::NEGP1) {
pred_opr.num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::NEGP0);
do_neg = !do_neg;
}
spv::Id pred_v = get_spirv_translator_visitor()->load(pred_opr, 0b0001);
if (do_neg) {
std::vector<spv::Id> ops{ pred_v };
pred_v = b.createOp(spv::OpLogicalNot, b.makeBoolType(), ops);
}
return pred_v;
}
void USSERecompilerSpirv::compile_break_node(const usse::USSEBreakNode &node) {
std::unique_ptr<spv::Builder::If> cond_builder;
if (node.get_condition() != 0) {
spv::Id pred_v = get_condition_value(node.get_condition());
cond_builder = std::make_unique<spv::Builder::If>(pred_v, spv::SelectionControlMaskNone, b);
}
b.createLoopExit();
if (cond_builder)
cond_builder->makeEndIf();
}
void USSERecompilerSpirv::compile_continue_node(const usse::USSEContinueNode &node) {
std::unique_ptr<spv::Builder::If> cond_builder;
if (node.get_condition() != 0) {
spv::Id pred_v = get_condition_value(node.get_condition());
cond_builder = std::make_unique<spv::Builder::If>(pred_v, spv::SelectionControlMaskNone, b);
}
b.createLoopContinue();
if (cond_builder)
cond_builder->makeEndIf();
}
void USSERecompilerSpirv::compile_conditional_node(const usse::USSEConditionalNode &cond) {
spv::Builder::If if_builder(get_condition_value(cond.negif_condition(), true), spv::SelectionControlMaskNone, b);
compile_block(*cond.if_block());
if (cond.else_block()) {
if_builder.makeBeginElse();
compile_block(*cond.else_block());
}
if_builder.makeEndIf();
}
void USSERecompilerSpirv::compile_loop_node(const usse::USSELoopNode &loop) {
spv::Builder::LoopBlocks loops = b.makeNewLoop();
b.createBranch(&loops.head);
b.setBuildPoint(&loops.head);
// In the head we only want to branch to body. We always do while do anyway
b.createLoopMerge(&loops.merge, &loops.head, 0, {});
b.createBranch(&loops.body);
// Emit body content
b.setBuildPoint(&loops.body);
// If true
const usse::USSEBlockNode &content_block = *(loop.content_block());
compile_block(content_block);
b.createBranch(&loops.continue_target);
// Emit continue target
b.setBuildPoint(&loops.continue_target);
b.createBranch(&loops.head);
// Merge point
b.setBuildPoint(&loops.merge);
b.closeLoop();
}
void USSERecompilerSpirv::begin_condition(const int cond) {
cond_stacks.emplace(get_condition_value(cond), spv::SelectionControlMaskNone, b);
}
void USSERecompilerSpirv::end_condition() {
cond_stacks.top().makeEndIf();
cond_stacks.pop();
}
spv::Function *USSERecompilerSpirv::compile_program_function() {
// Make a new function (subroutine)
spv::Block *last_build_point = b.getBuildPoint();
spv::Block *new_sub_block = nullptr;
const auto sub_name = fmt::format("{}_program", visitor->is_translating_secondary_program() ? "secondary" : "primary");
spv::Function *ret_func = b.makeFunctionEntry(spv::NoPrecision, b.makeVoidType(), sub_name.c_str(), {}, {},
&new_sub_block);
compile_block(tree_block_node);
b.leaveFunction();
b.setBuildPoint(last_build_point);
return ret_func;
}
void convert_gxp_usse_to_spirv(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features, const SpirvShaderParameters &parameters, utils::SpirvUtilFunctions &utils,
spv::Function *begin_hook_func, spv::Function *end_hook_func, const NonDependentTextureQueryCallInfos &queries, const spv::Id render_info_id) {
const uint64_t *primary_program = program.primary_program_start();
const uint64_t primary_program_instr_count = program.primary_program_instr_count;
const uint64_t *secondary_program_start = program.secondary_program_start();
const uint64_t *secondary_program_end = program.secondary_program_end();
std::map<ShaderPhase, std::pair<const std::uint64_t *, std::uint64_t>> shader_code;
// Collect instructions of Pixel (primary) phase
shader_code[ShaderPhase::Pixel] = std::make_pair(primary_program, primary_program_instr_count);
// Collect instructions of Sample rate (secondary) phase
shader_code[ShaderPhase::SampleRate] = std::make_pair(secondary_program_start, secondary_program_end - secondary_program_start);
if (begin_hook_func)
b.createFunctionCall(begin_hook_func, {});
// Decode and recompile
// TODO: Reuse this
usse::USSERecompilerSpirv recomp(b, program, features, parameters, utils, queries, render_info_id);
// Set the program
recomp.program = &program;
for (auto phase = 0; phase < (uint32_t)ShaderPhase::Max; ++phase) {
const auto cur_phase_code = shader_code[(ShaderPhase)phase];
if (cur_phase_code.second != 0) {
if (static_cast<ShaderPhase>(phase) == ShaderPhase::SampleRate) {
recomp.visitor->set_secondary_program(true);
} else {
recomp.visitor->set_secondary_program(false);
}
recomp.reset(cur_phase_code.first, cur_phase_code.second);
b.createFunctionCall(recomp.compile_program_function(), {});
}
}
// We reach the end
// Call end hook. If it's discard, this is not even called, so no worry
b.createFunctionCall(end_hook_func, {});
std::vector<spv::Id> empty_args;
if (features.should_use_shader_interlock() && program.is_fragment() && program.is_frag_color_used())
b.createNoResultOp(spv::OpEndInvocationInterlockEXT);
}
} // namespace shader::usse
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,235 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/spirv/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
spv::Id USSETranslatorVisitorSpirv::vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask) {
// Usually we would expect this to have a compare behavior
// Comparision is done by subtracting the first src by the second src, and compare the result value.
// We currently optimize for that case first
const DataType load_data_type = inst.opr.src1.type;
const DataType store_data_type = inst.opr.dest.type;
static const spv::Op tb_comp_ops[3][2][4] = {
{ { spv::OpFOrdNotEqual,
spv::OpFOrdLessThan,
spv::OpFOrdGreaterThan,
spv::OpAll },
{ spv::OpFOrdEqual,
spv::OpFOrdLessThanEqual,
spv::OpFOrdGreaterThanEqual,
spv::OpAll } },
{ { spv::OpINotEqual,
spv::OpSLessThan,
spv::OpSGreaterThan,
spv::OpAll },
{ spv::OpIEqual,
spv::OpSLessThanEqual,
spv::OpSGreaterThanEqual,
spv::OpAll } },
{ { spv::OpINotEqual,
spv::OpULessThan,
spv::OpUGreaterThan,
spv::OpAll },
{ spv::OpIEqual,
spv::OpULessThanEqual,
spv::OpUGreaterThanEqual,
spv::OpAll } },
};
// Load our compares
spv::Id lhs = spv::NoResult;
spv::Id rhs = spv::NoResult;
const spv::Id pred_type = utils::make_vector_or_scalar_type(m_b, m_b.makeBoolType(), mask ? 4 : 1);
spv::Id pred_result = utils::make_uniform_vector_from_type(m_b, m_b.makeBoolType(), true);
// Zero test number:
// 0 - alway pass
// 1 - zero
// 2 - non-zero
const bool compare_include_equal = (zero_test == 1);
// Sign test number
int index_tb_comp = 0;
if (is_signed_integer_data_type(load_data_type)) {
index_tb_comp = 1;
} else if (is_unsigned_integer_data_type(load_data_type)) {
index_tb_comp = 2;
}
const spv::Op used_comp_op = tb_comp_ops[index_tb_comp][compare_include_equal][sign_test];
// Optimize this case. Alternative name is CMP.
const char *tb_comp_str[2][4] = {
{ "ne",
"lt",
"gt",
"inv" },
{ "equal",
"le",
"ge",
"inv" }
};
const char *used_comp_str = tb_comp_str[compare_include_equal][sign_test];
m_b.setLine(m_recompiler.cur_pc);
if (is_sub_opcode(inst.opcode)) {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), "CMPMSK", used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask),
disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), "CMP", used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = load(inst.opr.src1, load_mask);
rhs = load(inst.opr.src2, load_mask);
} else {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}zero.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}zero.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = do_alu_op(inst, load_mask, mask ? mask : 0b1);
const spv::Id c0_type = utils::make_vector_or_scalar_type(m_b, m_b.makeFloatType(32), mask ? 4 : 1);
spv::Id c0 = utils::make_uniform_vector_from_type(m_b, c0_type, 0.0f);
if (is_signed_integer_data_type(load_data_type)) {
c0 = m_b.makeIntConstant(0);
} else if (is_unsigned_integer_data_type(load_data_type)) {
c0 = m_b.makeUintConstant(0);
}
rhs = c0;
}
if (lhs == spv::NoResult || rhs == spv::NoResult) {
LOG_ERROR("Source not loaded (lhs: {}, rhs: {})", lhs, rhs);
return spv::NoResult;
}
return m_b.createOp(used_comp_op, pred_type, { lhs, rhs });
}
bool USSETranslatorVisitorSpirv::vtst(
ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 src1_neg,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm3 chan_cc,
Imm2 pdst_n,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vtst(pred, skipinv, onceonly, syncstart, dest_ext, src1_neg, src1_ext, src2_ext,
prec, src2_vscomp, rpt_count, sign_test, zero_test, test_crcomb_and, chan_cc, pdst_n, dest_bank,
src1_bank, src2_bank, dest_n, test_wben, alu_sel, alu_op, src1_n, src2_n)) {
return false;
}
const spv::Id pred_result = vtst_impl(decoded_inst, pred, zero_test, sign_test, decoded_source_mask, false);
store(decoded_inst.opr.dest, pred_result);
return true;
}
bool USSETranslatorVisitorSpirv::vtstmsk(
ExtPredicate pred,
Imm1 skipinv,
Imm1 onceonly,
Imm1 syncstart,
Imm1 dest_ext,
Imm1 test_flag_2,
Imm1 src1_ext,
Imm1 src2_ext,
Imm1 prec,
Imm1 src2_vscomp,
RepeatCount rpt_count,
Imm2 sign_test,
Imm2 zero_test,
Imm1 test_crcomb_and,
Imm2 tst_mask_type,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm1 test_wben,
Imm2 alu_sel,
Imm4 alu_op,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vtstmsk(pred, skipinv, onceonly, syncstart, dest_ext, test_flag_2, src1_ext,
src2_ext, prec, src2_vscomp, rpt_count, sign_test, zero_test, test_crcomb_and, tst_mask_type,
dest_bank, src1_bank, src2_bank, dest_n, test_wben, alu_sel, alu_op, src1_n, src2_n)) {
return false;
}
spv::Id pred_result = vtst_impl(decoded_inst, pred, zero_test, sign_test, 0b1111, true);
spv::Id float_v4 = utils::make_vector_or_scalar_type(m_b, m_b.makeFloatType(32), 4);
spv::Id uint_v4 = utils::make_vector_or_scalar_type(m_b, m_b.makeUintType(32), 4);
spv::Id scaler;
switch (decoded_inst.opr.dest.type) {
case DataType::UINT8:
// OpSelect doesn't work UConvert doesn't work in glsl transpiler
pred_result = m_b.createUnaryOp(spv::OpFConvert, float_v4, pred_result);
pred_result = m_b.createUnaryOp(spv::OpConvertFToU, uint_v4, pred_result);
scaler = m_b.makeIntConstant(0xFF);
pred_result = m_b.createBinOp(spv::OpIMul, uint_v4, pred_result, scaler);
break;
case DataType::F16:
case DataType::F32:
pred_result = m_b.createUnaryOp(spv::OpFConvert, float_v4, pred_result);
break;
}
store(decoded_inst.opr.dest, pred_result);
return true;
}
+417
View File
@@ -0,0 +1,417 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv/translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <util/log.h>
#include <numeric>
using namespace shader;
using namespace usse;
bool USSETranslatorVisitorSpirv::vmov(
ExtPredicate pred,
bool skipinv,
Imm1 test_bit_2,
Imm1 src0_comp_sel,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end_or_src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
MoveType move_type,
RepeatCount repeat_count,
bool nosched,
DataType move_data_type,
Imm1 test_bit_1,
Imm4 src0_swiz,
Imm1 src0_bank_sel,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm4 dest_mask,
Imm6 dest_n,
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) {
if (!USSETranslatorVisitor::vmov(pred, skipinv, test_bit_2, src0_comp_sel, syncstart, dest_bank_ext, end_or_src0_bank_ext,
src1_bank_ext, src2_bank_ext, move_type, repeat_count, nosched, move_data_type, test_bit_1, src0_swiz,
src0_bank_sel, dest_bank_sel, src1_bank_sel, src2_bank_sel, dest_mask, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
const bool is_conditional = (move_type != MoveType::UNCONDITIONAL);
const bool is_u8_conditional = decoded_inst.opcode == Opcode::VMOVCU8;
// TODO: adjust dest mask if needed
CompareMethod compare_method = CompareMethod::NE_ZERO;
spv::Op compare_op = spv::OpAny;
if (is_conditional) {
compare_method = static_cast<CompareMethod>((test_bit_2 << 1) | test_bit_1);
switch (compare_method) {
case CompareMethod::LT_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpULessThan;
else
compare_op = spv::Op::OpFOrdLessThan;
break;
case CompareMethod::LTE_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpULessThanEqual;
else
compare_op = spv::Op::OpFOrdLessThanEqual;
break;
case CompareMethod::NE_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpINotEqual;
else
compare_op = spv::Op::OpFOrdNotEqual;
break;
case CompareMethod::EQ_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpIEqual;
else
compare_op = spv::Op::OpFOrdEqual;
break;
}
}
// Recompile
m_b.setLine(m_recompiler.cur_pc);
if ((move_data_type == DataType::F16) || (move_data_type == DataType::F32)) {
set_repeat_multiplier(2, 2, 2, 2);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
spv::Id source_to_compare_with_0 = spv::NoResult;
spv::Id source_1 = load(decoded_inst.opr.src1, decoded_dest_mask, src1_repeat_offset);
spv::Id source_2 = spv::NoResult;
spv::Id result = spv::NoResult;
if (source_1 == spv::NoResult) {
LOG_ERROR("Source not Loaded");
return false;
}
if (is_conditional) {
source_to_compare_with_0 = load(decoded_inst.opr.src0, decoded_dest_mask, src0_repeat_offset);
source_2 = load(decoded_inst.opr.src2, decoded_dest_mask, src2_repeat_offset);
spv::Id result_type = m_b.getTypeId(source_2);
spv::Id v0_comp_type = is_u8_conditional ? m_b.makeUintType(32) : m_b.makeFloatType(32);
spv::Id v0_type = utils::make_vector_or_scalar_type(m_b, v0_comp_type, m_b.getNumComponents(source_2));
spv::Id v0 = utils::make_uniform_vector_from_type(m_b, v0_type, 0);
bool source_2_first = false;
if (compare_op != spv::OpAny) {
// Merely do what the instruction does
// First compare source0 with vector 0
spv::Id cond_result = m_b.createOp(compare_op, m_b.makeVectorType(m_b.makeBoolType(), m_b.getNumComponents(source_to_compare_with_0)),
{ source_to_compare_with_0, v0 });
// For each component, if the compare result is true, move the equivalent component from source1 to dest,
// else the same thing with source2
// This behavior matches with OpSelect, so use it. Since IMix doesn't exist (really)
result = m_b.createOp(spv::OpSelect, result_type, { cond_result, source_1, source_2 });
} else {
// We optimize the float case. We can make the GPU use native float instructions without touching bool or integers
// Taking advantage of the mix function: if we use absolute 0 and 1 as the lerp, we got the equivalent of:
// mix(a, b, c) with c.comp is either 0 or 1 <=> if c.comp == 0 return a else return b.
switch (compare_method) {
case CompareMethod::LT_ZERO: {
// For each component: if source0.comp < 0 return 0 else return 1
// That means if we use mix, it should be mix(src1, src2, step_result)
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450Step, { v0, source_to_compare_with_0 });
source_2_first = false;
break;
}
case CompareMethod::LTE_ZERO: {
// For each component: if 0 < source0.comp return 0 else return 1
// Or, if we turn it around: if source0.comp <= 0 return 1 else return 0
// That means if we use mix, it should be mix(src2, src1, step_result)
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450Step, { source_to_compare_with_0, v0 });
source_2_first = true;
break;
}
case CompareMethod::NE_ZERO:
case CompareMethod::EQ_ZERO: {
// Taking advantage of the sign and absolute instruction
// The sign instruction returns 0 if the component equals to 0, else 1 if positive, -1 if negative
// That means if we absolute the sign result, we got 0 if component equals to 0, else we got 1.
// src2 will be first for Not equal case.
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FSign, { source_to_compare_with_0 });
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FAbs, { result });
if (compare_method == CompareMethod::NE_ZERO) {
source_2_first = true;
}
break;
}
default: {
LOG_ERROR("Unknown compare method: {}", static_cast<int>(compare_method));
return false;
}
}
// Mixing!! I'm like a little witch!!
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FMix, { source_2_first ? source_2 : source_1, source_2_first ? source_1 : source_2, result });
}
} else {
result = source_1;
}
store(decoded_inst.opr.dest, result, decoded_dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorSpirv::vpck(
ExtPredicate pred,
bool skipinv,
bool nosched,
Imm1 unknown,
bool syncstart,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
RepeatCount repeat_count,
Imm3 src_fmt,
Imm3 dest_fmt,
Imm4 dest_mask,
Imm2 dest_bank_sel,
Imm2 src1_bank_sel,
Imm2 src2_bank_sel,
Imm7 dest_n,
Imm2 comp_sel_3,
Imm1 scale,
Imm2 comp_sel_1,
Imm2 comp_sel_2,
Imm6 src1_n,
Imm1 comp0_sel_bit1,
Imm6 src2_n,
Imm1 comp_sel_0_bit0) {
if (!USSETranslatorVisitor::vpck(pred, skipinv, nosched, unknown, syncstart, dest_bank_ext,
end, src1_bank_ext, src2_bank_ext, repeat_count, src_fmt, dest_fmt, dest_mask, dest_bank_sel,
src1_bank_sel, src2_bank_sel, dest_n, comp_sel_3, scale, comp_sel_1, comp_sel_2, src1_n,
comp0_sel_bit1, src2_n, comp_sel_0_bit0)) {
return false;
}
// Recompile
m_b.setLine(m_recompiler.cur_pc);
// Doing this extra dest type check for future change in case I'm wrong (pent0)
if (is_integer_data_type(decoded_inst.opr.dest.type)) {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
} else {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
spv::Id source = load(decoded_inst.opr.src1, dest_mask, src1_repeat_offset);
if (source == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
if (decoded_inst.opr.src2.type != DataType::UNK) {
Operand src1 = decoded_inst.opr.src1;
Operand src2 = decoded_inst.opr.src2;
src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
src2.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id source1 = load(src1, 0b11, src1_repeat_offset);
spv::Id source2 = load(src2, 0b11, src2_repeat_offset);
source = utils::finalize(m_b, source1, source2, decoded_inst.opr.src1.swizzle, 0, dest_mask);
}
// source is float destination is int
if (is_float_data_type(decoded_inst.opr.dest.type) && !is_float_data_type(decoded_inst.opr.src1.type)) {
source = utils::convert_to_float(m_b, source, decoded_inst.opr.src1.type, scale);
}
// source is int destination is float
if (!is_float_data_type(decoded_inst.opr.dest.type) && is_float_data_type(decoded_inst.opr.src1.type)) {
source = utils::convert_to_int(m_b, source, decoded_inst.opr.dest.type, scale);
}
store(decoded_inst.opr.dest, source, dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorSpirv::vldst(
Imm2 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 moe_expand,
Imm1 sync_start,
Imm1 cache_ext,
Imm1 src0_bank_ext,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm4 mask_count,
Imm2 addr_mode,
Imm2 mode,
Imm1 dest_bank_primattr,
Imm1 range_enable,
Imm2 data_type,
Imm1 increment_or_decrement,
Imm1 src0_bank,
Imm1 cache_by_pass12,
Imm1 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vldst(op1, pred, skipinv, nosched, moe_expand, sync_start, cache_ext, src0_bank_ext,
src1_bank_ext, src2_bank_ext, mask_count, addr_mode, mode, dest_bank_primattr, range_enable, data_type,
increment_or_decrement, src0_bank, cache_by_pass12, drc_sel, src1_bank, src2_bank, dest_n, src0_n, src1_n,
src2_n)) {
return false;
}
// TODO:
// - Store instruction
// - Post or pre or any increment mode.
DataType type_to_ldst = DataType::UNK;
switch (data_type) {
case 0:
type_to_ldst = DataType::F32;
break;
case 1:
type_to_ldst = DataType::F16;
break;
case 2:
type_to_ldst = DataType::C10;
break;
default:
break;
}
const int total_number_to_fetch = mask_count + 1;
const int total_bytes_fo_fetch = get_data_type_size(type_to_ldst) * total_number_to_fetch;
// TODO: is source_2 in word or byte? Is it even used at all?
spv::Id source_0 = load(decoded_inst.opr.src0, 0b1, 0);
if (decoded_inst.opr.src1.bank == RegisterBank::IMMEDIATE) {
decoded_inst.opr.src1.num *= get_data_type_size(type_to_ldst);
}
spv::Id source_1 = load(decoded_inst.opr.src1, 0b1, 0);
spv::Id source_2 = load(decoded_inst.opr.src2, 0b1, 0);
// Seems that if it's indexed by register, offset is in bytes and based on 0x10000?
// Maybe that's just how the memory map operates. I'm not sure. However the literals on all shader so far is that
// Another thing is that, when moe expand is not enable, there seems to be 4 bytes added before fetching... No absolute prove.
// Maybe moe expand means it's not fetching after all? Dunno
std::uint32_t REG_INDEX_BASE = 0x10000;
spv::Id reg_index_base_cst = m_b.makeIntConstant(REG_INDEX_BASE);
if (decoded_inst.opr.src1.bank != shader::usse::RegisterBank::IMMEDIATE) {
source_1 = m_b.createBinOp(spv::OpISub, m_b.getTypeId(source_1), source_1, reg_index_base_cst);
}
spv::Id i32_type = m_b.makeIntType(32);
spv::Id base = m_b.createBinOp(spv::OpIAdd, i32_type, source_0, source_1);
base = m_b.createBinOp(spv::OpIAdd, i32_type, base, source_2);
if (!moe_expand) {
base = m_b.createBinOp(spv::OpIAdd, i32_type, base, m_b.makeIntConstant(4));
}
for (int i = 0; i < total_bytes_fo_fetch / 4; ++i) {
spv::Id offset = m_b.createBinOp(spv::OpIAdd, m_b.makeIntType(32), base, m_b.makeIntConstant(4 * i));
spv::Id src = utils::fetch_memory(m_b, m_spirv_params, m_util_funcs, offset);
store(decoded_inst.opr.dest, src, 0b1);
decoded_inst.opr.dest.num += 1;
}
return true;
}
bool USSETranslatorVisitorSpirv::limm(
bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
Imm6 imm_value_bits26to31,
ExtPredicate pred,
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits) {
Instruction inst;
inst.opcode = Opcode::MOV;
std::uint32_t imm_value = imm_value_first_21bits | (imm_value_bits21to25 << 21) | (imm_value_bits26to31 << 26);
spv::Id const_imm_id = m_b.makeUintConstant(imm_value);
inst.dest_mask = 0b1;
inst.opr.dest = decode_dest(inst.opr.dest, dest_num, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.dest.type = DataType::UINT32;
const std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode));
LOG_DISASM("{} {} #0x{:X}", disasm_str, disasm::operand_to_str(inst.opr.dest, 0b1, 0), imm_value);
store(inst.opr.dest, const_imm_id, 0b1);
return true;
}
+401
View File
@@ -0,0 +1,401 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv/translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
bool USSETranslatorVisitorSpirv::vbw(
Imm3 op1,
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
bool repeat_sel,
Imm1 sync_start,
Imm1 dest_ext,
Imm1 end,
Imm1 src1_ext,
Imm1 src2_ext,
RepeatCount repeat_count,
Imm1 src2_invert,
Imm5 src2_rot,
Imm2 src2_exth,
Imm1 op2,
Imm1 bitwise_partial,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src2_sel,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::vbw(op1, pred, skipinv, nosched, repeat_sel, sync_start, dest_ext, end, src1_ext, src2_ext,
repeat_count, src2_invert, src2_rot, src2_exth, op2, bitwise_partial, dest_bank, src1_bank, src2_bank,
dest_n, src2_sel, src1_n, src2_n)) {
return false;
}
set_repeat_multiplier(1, 1, 1, 1);
BEGIN_REPEAT(repeat_count)
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
bool immediate = src2_ext && decoded_inst.opr.src2.bank == RegisterBank::IMMEDIATE;
uint32_t value = 0;
if (src2_rot) {
LOG_WARN("Bitwise Rotations are unsupported.");
return false;
}
spv::Id src2 = 0;
if (immediate) {
value = src2_n | (static_cast<uint32_t>(src2_sel) << 7) | (static_cast<uint32_t>(src2_exth) << 14);
src2 = m_b.makeUintConstant(src2_invert ? ~value : value);
} else {
src2 = load(decoded_inst.opr.src2, 0b0001, src2_repeat_offset);
if (src2 == spv::NoResult) {
LOG_ERROR("Source 2 not loaded");
return false;
}
if (src2_invert) {
src2 = m_b.createUnaryOp(spv::Op::OpNot, type_ui32, src2);
}
}
spv::Id result;
spv::Op operation;
switch (decoded_inst.opcode) {
case Opcode::OR: operation = spv::Op::OpBitwiseOr; break;
case Opcode::AND: operation = spv::Op::OpBitwiseAnd; break;
case Opcode::XOR: operation = spv::Op::OpBitwiseXor; break;
case Opcode::ROL:
LOG_WARN("Bitwise Rotate Left operation unsupported.");
return false; // TODO: SPIRV doesn't seem to have a rotate left operation!
case Opcode::ASR: operation = spv::Op::OpShiftRightArithmetic; break;
case Opcode::SHL: operation = spv::Op::OpShiftLeftLogical; break;
case Opcode::SHR: operation = spv::Op::OpShiftRightLogical; break;
default: return false;
}
// optimisation. (any OR 0 || any XOR 0 || any AND 0xFFFFFFFF) -> assign
bool is_const = m_b.getOpCode(src2) == spv::Op::OpConstant;
auto const_val = is_const ? m_b.getConstantScalar(src2) : 1; // default value is intentionally non zero
if ((operation == spv::Op::OpBitwiseOr || operation == spv::Op::OpBitwiseXor) && is_const && const_val == 0
|| operation == spv::Op::OpBitwiseAnd && is_const && const_val == std::numeric_limits<decltype(const_val)>::max()) {
decoded_inst.opr.src1.type = DataType::F32;
decoded_inst.opr.dest.type = DataType::F32;
result = load(decoded_inst.opr.src1, 0b0001, src1_repeat_offset);
if (result == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
} else {
spv::Id src1 = load(decoded_inst.opr.src1, 0b0001, src1_repeat_offset);
if (src1 == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
result = m_b.createBinOp(operation, type_ui32, src1, src2);
if (m_b.isFloatType(m_b.getTypeId(src2))) {
result = m_b.createUnaryOp(spv::Op::OpBitcast, type_f32, src2);
}
}
store(decoded_inst.opr.dest, result, 0b0001, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
}
bool USSETranslatorVisitorSpirv::i8mad(
Imm2 pred,
Imm1 cmod1,
Imm1 skipinv,
Imm1 nosched,
Imm2 csel0,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm1 cmod2,
Imm3 repeat_count,
Imm1 saturated,
Imm1 cmod0,
Imm1 asel0,
Imm1 amod2,
Imm1 amod1,
Imm1 amod0,
Imm1 csel1,
Imm1 csel2,
Imm1 src0_neg,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_num,
Imm7 src0_num,
Imm7 src1_num,
Imm7 src2_num) {
if (!USSETranslatorVisitor::i8mad(pred, cmod1, skipinv, nosched, csel0, dest_bank_ext, end, src1_bank_ext,
src2_bank_ext, cmod2, repeat_count, saturated, cmod0, asel0, amod2, amod1, amod0, csel1, csel2, src0_neg,
src0_bank, dest_bank, src1_bank, src2_bank, dest_num, src0_num, src1_num, src2_num)) {
return false;
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id src1_mul = load(decoded_inst.opr.src1, 0b1111, src1_repeat_offset);
spv::Id src2_mul = load(decoded_inst.opr.src2, 0b1111, src2_repeat_offset);
spv::Id src0_add = load(decoded_inst.opr.src0, 0b1111, src0_repeat_offset);
spv::Id final_add = src0_add;
usse::Swizzle3 add_swizz_rgb = SWIZZLE_CHANNEL_3_DEFAULT;
bool add_swizz_rgb_src0 = true;
if ((csel0 != 0) || (asel0 != 0)) {
// We build source0 (the add component in this loop)
// Using OpVectorShuffle to construct the final one.
std::vector<spv::Id> shuffle_ops = { src0_add, src1_mul };
switch (csel0) {
case 0:
shuffle_ops.insert(shuffle_ops.end(), { 0, 1, 2 });
break;
case 1:
// Use src1 rgb
shuffle_ops.insert(shuffle_ops.end(), { 4, 5, 6 });
add_swizz_rgb_src0 = false;
break;
case 2:
// Use src0 full alpha
shuffle_ops.insert(shuffle_ops.end(), { 3, 3, 3 });
add_swizz_rgb = { usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W };
break;
case 3:
// Use src1 full alpha
shuffle_ops.insert(shuffle_ops.end(), { 7, 7, 7 });
add_swizz_rgb = { usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W };
add_swizz_rgb_src0 = false;
break;
default:
assert(false);
break;
}
switch (asel0) {
case 0:
// Use src0 alpha
shuffle_ops.push_back(3);
break;
case 1:
// Use src1 alpha
shuffle_ops.push_back(7);
break;
default:
assert(false);
break;
}
final_add = m_b.createOp(spv::OpVectorShuffle, m_b.getTypeId(src0_add), shuffle_ops);
}
spv::Id result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(src1_mul), src1_mul, src2_mul);
result = m_b.createBinOp(src0_neg ? spv::OpISub : spv::OpIAdd, m_b.getTypeId(src1_mul), result, final_add);
store(decoded_inst.opr.dest, result, 0b1111, dest_repeat_offset);
decoded_inst.opr.src0.swizzle[0] = add_swizz_rgb[0];
decoded_inst.opr.src0.swizzle[1] = add_swizz_rgb[1];
decoded_inst.opr.src0.swizzle[2] = add_swizz_rgb[2];
END_REPEAT();
return true;
}
bool USSETranslatorVisitorSpirv::i16mad(
ShortPredicate pred,
Imm1 abs,
Imm1 skipinv,
Imm1 nosched,
Imm1 src2_neg,
Imm1 sel1h_upper8,
Imm1 dest_bank_ext,
Imm1 end,
Imm1 src1_bank_ext,
Imm1 src2_bank_ext,
Imm3 repeat_count,
Imm2 mode,
Imm2 src2_format,
Imm2 src1_format,
Imm1 sel2h_upper8,
Imm2 or_shift,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i16mad(pred, abs, skipinv, nosched, src2_neg, sel1h_upper8, dest_bank_ext, end,
src1_bank_ext, src2_bank_ext, repeat_count, mode, src2_format, src1_format, sel2h_upper8, or_shift,
src0_bank, dest_bank, src1_bank, src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id source0 = load(decoded_inst.opr.src0, 0b11, src0_repeat_offset);
spv::Id source1 = load(decoded_inst.opr.src1, decoded_source_mask, src1_repeat_offset);
spv::Id source2 = load(decoded_inst.opr.src2, decoded_source_mask_2, src2_repeat_offset);
spv::Id source0_type = m_b.getTypeId(source0);
source1 = m_b.createCompositeConstruct(source0_type, { source1, source1 });
source2 = m_b.createCompositeConstruct(source0_type, { source2, source2 });
auto mul_result = m_b.createBinOp(spv::OpIMul, source0_type, source0, source1);
auto add_result = m_b.createBinOp(spv::OpIAdd, source0_type, mul_result, source2);
if (add_result != spv::NoResult) {
store(decoded_inst.opr.dest, add_result, 0b11, dest_repeat_offset);
}
END_REPEAT();
return true;
}
bool USSETranslatorVisitorSpirv::i32mad(
ShortPredicate pred,
Imm1 src0_high,
Imm1 nosched,
Imm1 src1_high,
Imm1 src2_high,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
RepeatCount repeat_count,
bool is_signed,
bool is_sat,
Imm2 src2_type,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i32mad(pred, src0_high, nosched, src1_high, src2_high, dest_bank_ext, end,
src1_bank_ext, src2_bank_ext, repeat_count, is_signed, is_sat, src2_type, src0_bank,
dest_bank, src1_bank, src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
spv::Id vsrc0 = load(decoded_inst.opr.src0, decoded_source_mask, 0);
spv::Id vsrc1 = load(decoded_inst.opr.src1, decoded_source_mask_2, 0);
spv::Id vsrc2 = load(decoded_inst.opr.src2, decoded_source_mask_3, 0);
auto mul_result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(vsrc0), vsrc0, vsrc1);
auto add_result = m_b.createBinOp(spv::OpIAdd, m_b.getTypeId(mul_result), mul_result, vsrc2);
if (add_result != spv::NoResult) {
store(decoded_inst.opr.dest, add_result, 0b1, 0);
}
return true;
}
bool USSETranslatorVisitorSpirv::i32mad2(
ExtPredicate pred,
Imm1 nosched,
Imm2 sn,
bool dest_bank_ext,
Imm1 end,
bool src1_bank_ext,
bool src2_bank_ext,
bool src0_bank_ext,
Imm3 count,
bool is_signed,
Imm1 negative_src1,
Imm1 negative_src2,
Imm1 src0_bank,
Imm2 dest_bank,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::i32mad2(pred, nosched, sn, dest_bank_ext, end, src1_bank_ext, src2_bank_ext,
src0_bank_ext, count, is_signed, negative_src1, negative_src2, src0_bank, dest_bank, src1_bank,
src2_bank, dest_n, src0_n, src1_n, src2_n)) {
return false;
}
spv::Id vsrc0 = load(decoded_inst.opr.src0, 0b1, 0);
spv::Id vsrc1 = load(decoded_inst.opr.src1, 0b1, 0);
spv::Id vsrc2 = load(decoded_inst.opr.src2, 0b1, 0);
auto mul_result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(vsrc0), vsrc0, vsrc1);
auto add_result = m_b.createBinOp(spv::OpIAdd, m_b.getTypeId(mul_result), mul_result, vsrc2);
// sn is mysterious argument.
// These are confirmed by hw testing:
// - pa = x * y + z (sn = 0) => pa = x * y + z
// - i = x * y + z (sn = 0) and then pa = x * y + i (sn = 1) => pa = x * y + z
// - pa = x * y + z (sn = 1) => crash
// TODO: properly implement this when we get more powerful fuzzer that can handle fpinternal.
if (sn == 0) {
store(decoded_inst.opr.dest, add_result, 0b1, 0);
} else {
store(decoded_inst.opr.dest, vsrc2, 0b1, 0);
}
return true;
}
@@ -0,0 +1,39 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv/translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
bool USSETranslatorVisitorSpirv::kill(
ShortPredicate pred) {
LOG_DISASM("{:016x}: KILL {}", m_instr, disasm::s_predicate_str(pred));
m_b.setLine(m_recompiler.cur_pc);
m_b.makeDiscard();
return true;
}
@@ -0,0 +1,281 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/spirv/translator.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <util/log.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
using namespace shader;
using namespace usse;
spv::Id shader::usse::USSETranslatorVisitorSpirv::do_fetch_texture(const spv::Id tex, const Coord &coord, const DataType dest_type, const int lod_mode, const spv::Id lod) {
auto coord_id = coord.first;
if (coord.second != static_cast<int>(DataType::F32)) {
coord_id = m_b.createOp(spv::OpVectorExtractDynamic, type_f32, { m_b.createLoad(coord_id, spv::NoPrecision), m_b.makeIntConstant(0) });
coord_id = utils::unpack_one(m_b, m_util_funcs, m_features, coord_id, static_cast<DataType>(coord.second));
// Shuffle if number of components is larger than 2
if (m_b.getNumComponents(coord_id) > 2) {
coord_id = m_b.createOp(spv::OpVectorShuffle, m_b.makeVectorType(type_f32, 2), { coord_id, coord_id, 0, 1 });
}
}
if (m_b.isPointer(coord_id)) {
coord_id = m_b.createLoad(coord_id, spv::NoPrecision);
}
assert(m_b.getTypeClass(m_b.getContainedTypeId(m_b.getTypeId(coord_id))) == spv::OpTypeFloat);
spv::Id image_sample = spv::NoResult;
if (lod == spv::NoResult) {
if (lod_mode == 4) {
image_sample = m_b.createOp(spv::OpImageSampleProjImplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id });
} else {
image_sample = m_b.createOp(spv::OpImageSampleImplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id });
}
} else {
if (lod_mode == 2) {
image_sample = m_b.createOp(spv::OpImageSampleExplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id, spv::ImageOperandsLodMask, lod });
} else if (lod_mode == 3) {
spv::Id ddx = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { lod, lod, 0, 1 });
spv::Id ddy = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { lod, lod, 2, 3 });
image_sample = m_b.createOp(spv::OpImageSampleExplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id, spv::ImageOperandsGradMask, ddx, ddy });
}
}
if (dest_type == DataType::F16) {
// Pack them
spv::Id pack1 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { image_sample, image_sample, 0, 1 });
pack1 = utils::pack_one(m_b, m_util_funcs, m_features, pack1, DataType::F16);
spv::Id pack2 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { image_sample, image_sample, 2, 3 });
pack2 = utils::pack_one(m_b, m_util_funcs, m_features, pack2, DataType::F16);
image_sample = m_b.createCompositeConstruct(type_f32_v[2], { pack1, pack2 });
}
if (dest_type == DataType::UINT8) {
image_sample = utils::convert_to_int(m_b, image_sample, DataType::UINT8, true);
image_sample = utils::pack_one(m_b, m_util_funcs, m_features, image_sample, DataType::UINT8);
}
return image_sample;
}
void shader::usse::USSETranslatorVisitorSpirv::do_texture_queries(const NonDependentTextureQueryCallInfos &texture_queries, const spv::Id translation_state_id) {
Operand store_op;
store_op.bank = RegisterBank::PRIMATTR;
store_op.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
store_op.type = DataType::F32;
for (auto &texture_query : texture_queries) {
Imm4 dest_mask;
switch (texture_query.store_type) {
case (int)DataType::F16: {
dest_mask = 0b11;
break;
}
case (int)DataType::F32: {
dest_mask = 0b1111;
break;
}
case (int)DataType::UINT8: {
dest_mask = 0b1;
break;
}
default:
assert(false);
}
bool proj = (texture_query.prod_pos >= 0);
shader::usse::Coord coord_inst = texture_query.coord;
if (texture_query.prod_pos >= 0) {
coord_inst.first = m_b.createOp(spv::OpVectorShuffle, type_f32_v[3], { texture_query.coord.first, texture_query.coord.first, 0, 1, static_cast<spv::Id>(texture_query.prod_pos) });
proj = true;
}
spv::Id fetch_result = do_fetch_texture(texture_query.sampler, coord_inst, static_cast<DataType>(texture_query.store_type), proj ? 4 : 0, 0);
store_op.num = texture_query.dest_offset;
if (static_cast<DataType>(texture_query.store_type) == DataType::UNK) {
// Manual check
spv::Id sampler_integral_query_format = m_b.createAccessChain(spv::StorageClassPrivate, translation_state_id, { m_b.makeIntConstant(4), m_b.makeIntConstant(texture_query.sampler_index / 4), m_b.makeIntConstant(texture_query.sampler_index % 4) });
spv::Id bool_type = m_b.makeBoolType();
spv::Builder::If if_builder(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeFloatConstant(INTEGRAL_TEX_QUERY_TYPE_8BIT_SIGNED)), spv::SelectionControlMaskNone, m_b);
spv::Id packed8 = utils::convert_to_int(m_b, fetch_result, DataType::INT8, true);
packed8 = utils::pack_one(m_b, m_util_funcs, m_features, packed8, DataType::INT8);
dest_mask = 0b1;
store(store_op, packed8, dest_mask);
if_builder.makeBeginElse();
spv::Builder::If if_builder_2(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeIntConstant(INTEGRAL_TEX_QUERY_TYPE_8BIT_UNSIGNED)), spv::SelectionControlMaskNone, m_b);
packed8 = utils::convert_to_int(m_b, fetch_result, DataType::UINT8, true);
packed8 = utils::pack_one(m_b, m_util_funcs, m_features, packed8, DataType::UINT8);
dest_mask = 0b1;
store(store_op, packed8, dest_mask);
if_builder_2.makeBeginElse();
spv::Builder::If if_builder_3(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeIntConstant(INTEGRAL_TEX_QUERY_TYPE_16BIT)), spv::SelectionControlMaskNone, m_b);
spv::Id pack1 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { fetch_result, fetch_result, 0, 1 });
pack1 = utils::pack_one(m_b, m_util_funcs, m_features, pack1, DataType::F16);
spv::Id pack2 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { fetch_result, fetch_result, 2, 3 });
pack2 = utils::pack_one(m_b, m_util_funcs, m_features, pack2, DataType::F16);
spv::Id packedu16 = m_b.createCompositeConstruct(type_f32_v[2], { pack1, pack2 });
dest_mask = 0b11;
store(store_op, packedu16, dest_mask);
if_builder_3.makeBeginElse();
dest_mask = 0b1111;
store(store_op, fetch_result, dest_mask);
if_builder_3.makeEndIf();
if_builder_2.makeEndIf();
if_builder.makeEndIf();
} else {
store(store_op, fetch_result, dest_mask);
}
}
}
bool USSETranslatorVisitorSpirv::smp(
ExtPredicate pred,
Imm1 skipinv,
Imm1 nosched,
Imm1 syncstart,
Imm1 minpack,
Imm1 src0_ext,
Imm1 src1_ext,
Imm1 src2_ext,
Imm2 fconv_type,
Imm2 mask_count,
Imm2 dim,
Imm2 lod_mode,
bool dest_use_pa,
Imm2 sb_mode,
Imm2 src0_type,
Imm1 src0_bank,
Imm2 drc_sel,
Imm2 src1_bank,
Imm2 src2_bank,
Imm7 dest_n,
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
if (!USSETranslatorVisitor::smp(pred, skipinv, nosched, syncstart, minpack, src0_ext, src2_ext, src2_ext, fconv_type,
mask_count, dim, lod_mode, dest_use_pa, sb_mode, src0_type, src0_bank, drc_sel, src1_bank, src2_bank,
dest_n, src0_n, src1_n, src2_n)) {
return false;
}
// LOD mode: none, bias, replace, gradient
if ((lod_mode != 0) && (lod_mode != 2) && (lod_mode != 3)) {
LOG_ERROR("Sampler LOD replace not implemented!");
return true;
}
// Base 0, turn it to base 1
dim += 1;
spv::Id coord_mask = 0b0011;
if (dim == 3) {
coord_mask = 0b0111;
} else if (dim == 1) {
coord_mask = 0b0001;
}
m_b.setLine(m_recompiler.cur_pc);
// Generate simple stuff
// Load the coord
spv::Id coord = load(decoded_inst.opr.src0, coord_mask);
if (coord == spv::NoResult) {
LOG_ERROR("Coord not loaded");
return false;
}
if (dim == 1) {
// It should be a line, so Y should be zero. There are only two dimensions texture, so this is a guess (seems concise)
coord = m_b.createCompositeConstruct(m_b.makeVectorType(m_b.makeFloatType(32), 2), { coord, m_b.makeIntConstant(0) });
}
spv::Id sampler = spv::NoResult;
if (m_spirv_params.samplers.count(decoded_inst.opr.src1.num)) {
sampler = m_spirv_params.samplers.at(decoded_inst.opr.src1.num);
} else {
LOG_ERROR("Can't get the sampler (sampler doesn't exist!)");
return true;
}
// Either LOD number or gradient number
spv::Id extra = spv::NoResult;
if (lod_mode != 0) {
switch (lod_mode) {
case 2:
extra = load(decoded_inst.opr.src2, 0b1);
break;
case 3:
extra = load(decoded_inst.opr.src2, 0b1111);
break;
default:
break;
}
}
spv::Id result = do_fetch_texture(sampler, { coord, static_cast<int>(DataType::F32) }, DataType::F32, lod_mode, extra);
switch (sb_mode) {
case 0:
case 1:
store(decoded_inst.opr.dest, result, 0b1111);
break;
case 3: {
// TODO: figure out what to fill here
// store(inst.opr.dest, stub, 0b1111);
store(decoded_inst.opr.dest, result, 0b1111);
break;
}
default: {
LOG_ERROR("Unsupported sb_mode: {}", sb_mode);
}
}
return true;
}
@@ -15,33 +15,31 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator.h>
#include <shader/spirv/translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_constant_table.h>
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/constant_table.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/types.h>
#include <util/log.h>
#include <gxm/functions.h>
#include <bitset>
using namespace shader;
using namespace usse;
spv::Id USSETranslatorVisitor::load(Operand op, const Imm4 dest_mask, const int shift_offset) {
spv::Id USSETranslatorVisitorSpirv::load(Operand op, const Imm4 dest_mask, const int shift_offset) {
return utils::load(m_b, m_spirv_params, m_util_funcs, m_features, op, dest_mask, shift_offset);
}
void USSETranslatorVisitor::store(Operand dest, spv::Id source, std::uint8_t dest_mask, int shift_offset) {
void USSETranslatorVisitorSpirv::store(Operand dest, spv::Id source, std::uint8_t dest_mask, int shift_offset) {
utils::store(m_b, m_spirv_params, m_util_funcs, m_features, dest, source, dest_mask, shift_offset);
}
spv::Id USSETranslatorVisitor::swizzle_to_spv_comp(spv::Id composite, spv::Id type, SwizzleChannel swizzle) {
spv::Id USSETranslatorVisitorSpirv::swizzle_to_spv_comp(spv::Id composite, spv::Id type, SwizzleChannel swizzle) {
switch (swizzle) {
case SwizzleChannel::C_X:
case SwizzleChannel::C_Y:
@@ -59,10 +57,4 @@ spv::Id USSETranslatorVisitor::swizzle_to_spv_comp(spv::Id composite, spv::Id ty
LOG_WARN("Swizzle channel {} unsupported", static_cast<Imm4>(swizzle));
return spv::NoResult;
}
size_t USSETranslatorVisitor::dest_mask_to_comp_count(Imm4 dest_mask) {
std::bitset<4> bs(dest_mask);
const auto bit_count = bs.count();
return bit_count;
}
}
@@ -15,9 +15,9 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_constant_table.h>
#include <shader/usse_program_analyzer.h>
#include <shader/usse_utilities.h>
#include <shader/constant_table.h>
#include <shader/program_analyzer.h>
#include <shader/spirv/utilities.h>
#include <util/log.h>
#include <SPIRV/GLSL.std.450.h>
@@ -97,13 +97,6 @@ spv::Id shader::usse::utils::finalize(spv::Builder &b, spv::Id first, spv::Id se
return b.createCompositeConstruct(b.makeVectorType(target_type, static_cast<int>(ops.size())), ops);
}
size_t shader::usse::utils::dest_mask_to_comp_count(shader::usse::Imm4 dest_mask) {
std::bitset<4> bs(dest_mask);
const auto bit_count = bs.count();
assert(bit_count <= 4 && bit_count > 0);
return bit_count;
}
static const shader::usse::SpirvVarRegBank *get_reg_bank(const shader::usse::SpirvShaderParameters &params, shader::usse::RegisterBank reg_bank) {
switch (reg_bank) {
case shader::usse::RegisterBank::PRIMATTR:
@@ -1,5 +1,5 @@
// Vita3K emulator project
// Copyright (C) 2022 Vita3K team
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,21 +15,17 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator_entry.h>
#include <gxm/types.h>
#include <shader/decoder_detail.h>
#include <shader/disasm.h>
#include <shader/matcher.h>
#include <shader/usse_disasm.h>
#include <shader/usse_translator.h>
#include <shader/usse_translator_types.h>
#include <shader/translator.h>
#include <util/log.h>
#include <util/optional.h>
#include <map>
#include <bitset>
namespace shader::usse {
template <typename Visitor>
using USSEMatcher = shader::decoder::Matcher<Visitor, uint64_t>;
@@ -829,60 +825,26 @@ static optional<const USSEMatcher<V>> DecodeUSSE(uint64_t instruction) {
#endif
}
//
// Decoder/translator usage
//
USSERecompiler::USSERecompiler(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features, const SpirvShaderParameters &parameters,
utils::SpirvUtilFunctions &utils, spv::Function *end_hook_func, const NonDependentTextureQueryCallInfos &queries, const spv::Id render_info_id)
: inst(nullptr)
USSERecompiler::USSERecompiler(const SceGxmProgram &program_)
: program(&program_)
, inst(nullptr)
, count(0)
, b(b)
, visitor(b, *this, program, features, utils, cur_instr, parameters, queries, render_info_id, true)
, end_hook_func(end_hook_func)
, visitor(nullptr)
, tree_block_node(nullptr, 0) {
}
void USSERecompiler::reset(const std::uint64_t *_inst, const std::size_t _count) {
inst = _inst;
count = _count;
visitor.reset_for_new_session();
visitor->reset_for_new_session();
usse::analyze(tree_block_node, static_cast<shader::usse::USSEOffset>(_count - 1),
[&](usse::USSEOffset off) -> std::uint64_t { return inst[off]; });
}
spv::Id USSERecompiler::get_condition_value(const std::uint8_t pred, const bool neg) {
const ExtPredicate predicator = static_cast<ExtPredicate>(pred);
Operand pred_opr{};
pred_opr.bank = RegisterBank::PREDICATE;
bool do_neg = neg;
if (predicator >= ExtPredicate::P0 && predicator <= ExtPredicate::P3) {
pred_opr.num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::P0);
} else if (predicator >= ExtPredicate::NEGP0 && predicator <= ExtPredicate::NEGP1) {
pred_opr.num = static_cast<int>(predicator) - static_cast<int>(ExtPredicate::NEGP0);
do_neg = !do_neg;
}
spv::Id pred_v = visitor.load(pred_opr, 0b0001);
if (do_neg) {
std::vector<spv::Id> ops{ pred_v };
pred_v = b.createOp(spv::OpLogicalNot, b.makeBoolType(), ops);
}
return pred_v;
}
void USSERecompiler::compile_code_node(const usse::USSECodeNode &code) {
std::unique_ptr<spv::Builder::If> cond_builder;
if (code.condition != 0) {
// Construct the IF
spv::Id pred_v = get_condition_value(code.condition);
cond_builder = std::make_unique<spv::Builder::If>(pred_v, spv::SelectionControlMaskNone, b);
begin_condition(code.condition);
}
const auto last_pc = cur_pc;
@@ -898,85 +860,17 @@ void USSERecompiler::compile_code_node(const usse::USSECodeNode &code) {
// Recompile the instruction, to the current block
auto decoder = usse::DecodeUSSE<usse::USSETranslatorVisitor>(cur_instr);
if (decoder.has_value())
decoder->call(visitor, cur_instr);
decoder->call(*visitor, cur_instr);
else
LOG_DISASM("{:016x}: error: instruction unmatched", cur_instr);
}
}
if (cond_builder) {
cond_builder->makeEndIf();
if (code.condition != 0) {
end_condition();
}
}
void USSERecompiler::compile_break_node(const usse::USSEBreakNode &node) {
std::unique_ptr<spv::Builder::If> cond_builder;
if (node.get_condition() != 0) {
spv::Id pred_v = get_condition_value(node.get_condition());
cond_builder = std::make_unique<spv::Builder::If>(pred_v, spv::SelectionControlMaskNone, b);
}
b.createLoopExit();
if (cond_builder)
cond_builder->makeEndIf();
}
void USSERecompiler::compile_continue_node(const usse::USSEContinueNode &node) {
std::unique_ptr<spv::Builder::If> cond_builder;
if (node.get_condition() != 0) {
spv::Id pred_v = get_condition_value(node.get_condition());
cond_builder = std::make_unique<spv::Builder::If>(pred_v, spv::SelectionControlMaskNone, b);
}
b.createLoopContinue();
if (cond_builder)
cond_builder->makeEndIf();
}
void USSERecompiler::compile_conditional_node(const usse::USSEConditionalNode &cond) {
spv::Builder::If if_builder(get_condition_value(cond.negif_condition(), true), spv::SelectionControlMaskNone, b);
compile_block(*cond.if_block());
if (cond.else_block()) {
if_builder.makeBeginElse();
compile_block(*cond.else_block());
}
if_builder.makeEndIf();
}
void USSERecompiler::compile_loop_node(const usse::USSELoopNode &loop) {
spv::Builder::LoopBlocks loops = b.makeNewLoop();
b.createBranch(&loops.head);
b.setBuildPoint(&loops.head);
// In the head we only want to branch to body. We always do while do anyway
b.createLoopMerge(&loops.merge, &loops.head, 0, {});
b.createBranch(&loops.body);
// Emit body content
b.setBuildPoint(&loops.body);
// If true
const usse::USSEBlockNode &content_block = *(loop.content_block());
compile_block(content_block);
b.createBranch(&loops.continue_target);
// Emit continue target
b.setBuildPoint(&loops.continue_target);
b.createBranch(&loops.head);
// Merge point
b.setBuildPoint(&loops.merge);
b.closeLoop();
}
void USSERecompiler::compile_block(const usse::USSEBlockNode &block) {
for (std::size_t i = 0; i < block.children_count(); i++) {
const usse::USSEBaseNode *node = block.children_at(i);
@@ -1009,72 +903,16 @@ void USSERecompiler::compile_block(const usse::USSEBlockNode &block) {
}
}
spv::Function *USSERecompiler::compile_program_function() {
// Make a new function (subroutine)
spv::Block *last_build_point = b.getBuildPoint();
spv::Block *new_sub_block = nullptr;
const auto sub_name = fmt::format("{}_program", visitor.is_translating_secondary_program() ? "secondary" : "primary");
spv::Function *ret_func = b.makeFunctionEntry(spv::NoPrecision, b.makeVoidType(), sub_name.c_str(), {}, {},
&new_sub_block);
compile_block(tree_block_node);
b.leaveFunction();
b.setBuildPoint(last_build_point);
return ret_func;
size_t USSETranslatorVisitor::dest_mask_to_comp_count(Imm4 dest_mask) {
std::bitset<4> bs(dest_mask);
const auto bit_count = bs.count();
return bit_count;
}
void convert_gxp_usse_to_spirv(spv::Builder &b, const SceGxmProgram &program, const FeatureState &features, const SpirvShaderParameters &parameters, utils::SpirvUtilFunctions &utils,
spv::Function *begin_hook_func, spv::Function *end_hook_func, const NonDependentTextureQueryCallInfos &queries, const spv::Id render_info_id) {
const uint64_t *primary_program = program.primary_program_start();
const uint64_t primary_program_instr_count = program.primary_program_instr_count;
const uint64_t *secondary_program_start = program.secondary_program_start();
const uint64_t *secondary_program_end = program.secondary_program_end();
std::map<ShaderPhase, std::pair<const std::uint64_t *, std::uint64_t>> shader_code;
// Collect instructions of Pixel (primary) phase
shader_code[ShaderPhase::Pixel] = std::make_pair(primary_program, primary_program_instr_count);
// Collect instructions of Sample rate (secondary) phase
shader_code[ShaderPhase::SampleRate] = std::make_pair(secondary_program_start, secondary_program_end - secondary_program_start);
if (begin_hook_func)
b.createFunctionCall(begin_hook_func, {});
// Decode and recompile
// TODO: Reuse this
usse::USSERecompiler recomp(b, program, features, parameters, utils, end_hook_func, queries, render_info_id);
// Set the program
recomp.program = &program;
for (auto phase = 0; phase < (uint32_t)ShaderPhase::Max; ++phase) {
const auto cur_phase_code = shader_code[(ShaderPhase)phase];
if (cur_phase_code.second != 0) {
if (static_cast<ShaderPhase>(phase) == ShaderPhase::SampleRate) {
recomp.visitor.set_secondary_program(true);
} else {
recomp.visitor.set_secondary_program(false);
}
recomp.reset(cur_phase_code.first, cur_phase_code.second);
b.createFunctionCall(recomp.compile_program_function(), {});
}
}
// We reach the end
// Call end hook. If it's discard, this is not even called, so no worry
b.createFunctionCall(end_hook_func, {});
std::vector<spv::Id> empty_args;
if (features.should_use_shader_interlock() && program.is_fragment() && program.is_frag_color_used())
b.createNoResultOp(spv::OpEndInvocationInterlockEXT);
size_t dest_mask_to_comp_count(shader::usse::Imm4 dest_mask) {
std::bitset<4> bs(dest_mask);
const auto bit_count = bs.count();
assert(bit_count <= 4 && bit_count > 0);
return bit_count;
}
} // namespace shader::usse
} // namespace shader::usse
File diff suppressed because it is too large Load Diff
+29 -176
View File
@@ -1,5 +1,6 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,15 +16,13 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_translator.h>
#include <shader/usse_types.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
namespace shader::usse {
static Opcode decode_test_inst(const Imm2 alu_sel, const Imm4 alu_op, const Imm1 prec, DataType &filled_dt) {
static const Opcode vf16_opcode[16] = {
Opcode::INVALID,
@@ -199,127 +198,6 @@ static Opcode decode_test_inst(const Imm2 alu_sel, const Imm4 alu_op, const Imm1
return test_op;
}
inline bool is_sub_opcode(Opcode test_op) {
return (test_op == Opcode::VSUB) || (test_op == Opcode::VF16SUB) || (test_op == Opcode::ISUB8) || (test_op == Opcode::ISUB16) || (test_op == Opcode::ISUB32) || (test_op == Opcode::ISUBU8) || (test_op == Opcode::ISUBU16) || (test_op == Opcode::ISUBU32) || (test_op == Opcode::FPSUB8);
}
spv::Id USSETranslatorVisitor::vtst_impl(Instruction inst, ExtPredicate pred, int zero_test, int sign_test, Imm4 load_mask, bool mask) {
// Usually we would expect this to have a compare behavior
// Comparision is done by subtracting the first src by the second src, and compare the result value.
// We currently optimize for that case first
const DataType load_data_type = inst.opr.src1.type;
const DataType store_data_type = inst.opr.dest.type;
static const spv::Op tb_comp_ops[3][2][4] = {
{ { spv::OpFOrdNotEqual,
spv::OpFOrdLessThan,
spv::OpFOrdGreaterThan,
spv::OpAll },
{ spv::OpFOrdEqual,
spv::OpFOrdLessThanEqual,
spv::OpFOrdGreaterThanEqual,
spv::OpAll } },
{ { spv::OpINotEqual,
spv::OpSLessThan,
spv::OpSGreaterThan,
spv::OpAll },
{ spv::OpIEqual,
spv::OpSLessThanEqual,
spv::OpSGreaterThanEqual,
spv::OpAll } },
{ { spv::OpINotEqual,
spv::OpULessThan,
spv::OpUGreaterThan,
spv::OpAll },
{ spv::OpIEqual,
spv::OpULessThanEqual,
spv::OpUGreaterThanEqual,
spv::OpAll } },
};
// Load our compares
spv::Id lhs = spv::NoResult;
spv::Id rhs = spv::NoResult;
const spv::Id pred_type = utils::make_vector_or_scalar_type(m_b, m_b.makeBoolType(), mask ? 4 : 1);
spv::Id pred_result = utils::make_uniform_vector_from_type(m_b, m_b.makeBoolType(), true);
// Zero test number:
// 0 - alway pass
// 1 - zero
// 2 - non-zero
const bool compare_include_equal = (zero_test == 1);
// Sign test number
int index_tb_comp = 0;
if (is_signed_integer_data_type(load_data_type)) {
index_tb_comp = 1;
} else if (is_unsigned_integer_data_type(load_data_type)) {
index_tb_comp = 2;
}
const spv::Op used_comp_op = tb_comp_ops[index_tb_comp][compare_include_equal][sign_test];
// Optimize this case. Alternative name is CMP.
const char *tb_comp_str[2][4] = {
{ "ne",
"lt",
"gt",
"inv" },
{ "equal",
"le",
"ge",
"inv" }
};
const char *used_comp_str = tb_comp_str[compare_include_equal][sign_test];
m_b.setLine(m_recompiler.cur_pc);
if (is_sub_opcode(inst.opcode)) {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), "CMPMSK", used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask),
disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), "CMP", used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = load(inst.opr.src1, load_mask);
rhs = load(inst.opr.src2, load_mask);
} else {
if (mask) {
LOG_DISASM("{:016x}: {}{}.{}zero.{}.{} {} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(store_data_type),
disasm::data_type_str(load_data_type), disasm::operand_to_str(inst.opr.dest, 0b1111), disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
} else {
LOG_DISASM("{:016x}: {}{}.{}zero.{} p{} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), used_comp_str, disasm::data_type_str(load_data_type),
inst.opr.dest.num, disasm::operand_to_str(inst.opr.src1, load_mask), disasm::operand_to_str(inst.opr.src2, load_mask));
}
lhs = do_alu_op(inst, load_mask, mask ? mask : 0b1);
const spv::Id c0_type = utils::make_vector_or_scalar_type(m_b, m_b.makeFloatType(32), mask ? 4 : 1);
spv::Id c0 = utils::make_uniform_vector_from_type(m_b, c0_type, 0.0f);
if (is_signed_integer_data_type(load_data_type)) {
c0 = m_b.makeIntConstant(0);
} else if (is_unsigned_integer_data_type(load_data_type)) {
c0 = m_b.makeUintConstant(0);
}
rhs = c0;
}
if (lhs == spv::NoResult || rhs == spv::NoResult) {
LOG_ERROR("Source not loaded (lhs: {}, rhs: {})", lhs, rhs);
return spv::NoResult;
}
return m_b.createOp(used_comp_op, pred_type, { lhs, rhs });
}
bool USSETranslatorVisitor::vtst(
ExtPredicate pred,
Imm1 skipinv,
@@ -354,8 +232,7 @@ bool USSETranslatorVisitor::vtst(
return false;
}
Instruction inst;
inst.opcode = test_op;
decoded_inst.opcode = test_op;
const Imm4 tb_decode_load_mask[] = {
0b0001,
@@ -372,33 +249,30 @@ bool USSETranslatorVisitor::vtst(
return false;
}
const Imm4 load_mask = tb_decode_load_mask[chan_cc];
decoded_source_mask = tb_decode_load_mask[chan_cc];
const bool use_double_reg = alu_sel == 0;
const uint8_t bits_max = use_double_reg ? 8 : 7;
// Build up source
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_ext, use_double_reg, bits_max, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_ext, use_double_reg, bits_max, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_ext, use_double_reg, bits_max, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_ext, use_double_reg, bits_max, m_second_program);
inst.opr.src1.type = load_data_type;
inst.opr.src2.type = load_data_type;
decoded_inst.opr.src1.type = load_data_type;
decoded_inst.opr.src2.type = load_data_type;
inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
inst.opr.src2.swizzle = (src2_vscomp && (alu_sel == 0)) ? (Swizzle4 SWIZZLE_CHANNEL_4(X, X, X, X)) : (Swizzle4 SWIZZLE_CHANNEL_4_DEFAULT);
decoded_inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.src2.swizzle = (src2_vscomp && (alu_sel == 0)) ? (Swizzle4 SWIZZLE_CHANNEL_4(X, X, X, X)) : (Swizzle4 SWIZZLE_CHANNEL_4_DEFAULT);
if (src1_neg) {
inst.opr.src1.flags |= RegisterFlags::Negative;
decoded_inst.opr.src1.flags |= RegisterFlags::Negative;
}
Operand pred_op{};
pred_op.bank = RegisterBank::PREDICATE;
pred_op.num = pdst_n;
inst.opr.dest = pred_op;
decoded_inst.opr.dest = pred_op;
const spv::Id pred_result = vtst_impl(inst, pred, zero_test, sign_test, load_mask, false);
store(inst.opr.dest, pred_result);
return true;
}
@@ -429,14 +303,13 @@ bool USSETranslatorVisitor::vtstmsk(
Imm7 src2_n) {
DataType load_data_type;
Opcode test_op = decode_test_inst(alu_sel, alu_op, prec, load_data_type);
Instruction inst;
if (test_op == Opcode::INVALID) {
LOG_ERROR("Unsupported comparision: {} {}", alu_sel, alu_op);
return false;
}
inst.opcode = test_op;
decoded_inst.opcode = test_op;
if (tst_mask_type == 3) {
LOG_ERROR("Invalid test mask type: 3");
@@ -461,20 +334,20 @@ bool USSETranslatorVisitor::vtstmsk(
const bool use_double_reg = alu_sel == 0;
// TODO: In some op, we don't need to load src2 e.g. rsq
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_ext, use_double_reg, 8, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_ext, use_double_reg, 8, m_second_program);
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank, dest_ext, use_double_reg, 8, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_ext, use_double_reg, 8, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_ext, use_double_reg, 8, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank, dest_ext, use_double_reg, 8, m_second_program);
inst.opr.src1.type = load_data_type;
inst.opr.src2.type = load_data_type;
inst.opr.dest.type = store_data_type;
decoded_inst.opr.src1.type = load_data_type;
decoded_inst.opr.src2.type = load_data_type;
decoded_inst.opr.dest.type = store_data_type;
inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
inst.opr.src2.swizzle = src2_vscomp ? (Swizzle4 SWIZZLE_CHANNEL_4(X, X, X, X)) : (Swizzle4 SWIZZLE_CHANNEL_4_DEFAULT);
inst.opr.dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.src2.swizzle = src2_vscomp ? (Swizzle4 SWIZZLE_CHANNEL_4(X, X, X, X)) : (Swizzle4 SWIZZLE_CHANNEL_4_DEFAULT);
decoded_inst.opr.dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
if (test_flag_2) {
inst.opr.src1.flags |= RegisterFlags::Negative;
decoded_inst.opr.src1.flags |= RegisterFlags::Negative;
}
if (!test_wben) {
@@ -483,27 +356,6 @@ bool USSETranslatorVisitor::vtstmsk(
return false;
}
spv::Id pred_result = vtst_impl(inst, pred, zero_test, sign_test, 0b1111, true);
spv::Id float_v4 = utils::make_vector_or_scalar_type(m_b, m_b.makeFloatType(32), 4);
spv::Id uint_v4 = utils::make_vector_or_scalar_type(m_b, m_b.makeUintType(32), 4);
spv::Id scaler;
switch (store_data_type) {
case DataType::UINT8:
// OpSelect doesn't work UConvert doesn't work in glsl transpiler
pred_result = m_b.createUnaryOp(spv::OpFConvert, float_v4, pred_result);
pred_result = m_b.createUnaryOp(spv::OpConvertFToU, uint_v4, pred_result);
scaler = m_b.makeIntConstant(0xFF);
pred_result = m_b.createBinOp(spv::OpIMul, uint_v4, pred_result, scaler);
break;
case DataType::F16:
case DataType::F32:
pred_result = m_b.createUnaryOp(spv::OpFConvert, float_v4, pred_result);
break;
}
store(inst.opr.dest, pred_result);
return true;
}
@@ -525,3 +377,4 @@ bool USSETranslatorVisitor::br(
LOG_ERROR("Branch instruction should not be recompiled here!");
return true;
}
} // namespace shader::usse
+84 -305
View File
@@ -1,5 +1,5 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,21 +15,13 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/translator.h>
#include <shader/types.h>
#include <util/log.h>
#include <numeric>
using namespace shader;
using namespace usse;
namespace shader::usse {
bool USSETranslatorVisitor::vmov(
ExtPredicate pred,
bool skipinv,
@@ -55,8 +47,6 @@ bool USSETranslatorVisitor::vmov(
Imm6 src0_n,
Imm6 src1_n,
Imm6 src2_n) {
Instruction inst;
static const Opcode tb_decode_vmov[] = {
Opcode::VMOV,
Opcode::VMOVC,
@@ -64,90 +54,61 @@ bool USSETranslatorVisitor::vmov(
Opcode::INVALID,
};
inst.opcode = tb_decode_vmov[(Imm3)move_type];
decoded_inst.opcode = tb_decode_vmov[(Imm3)move_type];
// TODO: dest mask
// TODO: flags
// TODO: test type
const bool is_double_regs = move_data_type == DataType::C10 || move_data_type == DataType::F16 || move_data_type == DataType::F32;
const bool is_conditional = (move_type != MoveType::UNCONDITIONAL);
const bool is_u8_conditional = inst.opcode == Opcode::VMOVCU8;
const bool is_u8_conditional = decoded_inst.opcode == Opcode::VMOVCU8;
// Decode operands
uint8_t reg_bits = is_double_regs ? 7 : 6;
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank_sel, dest_bank_ext, is_double_regs, reg_bits, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank_sel, src1_bank_ext, is_double_regs, reg_bits, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank_sel, dest_bank_ext, is_double_regs, reg_bits, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank_sel, src1_bank_ext, is_double_regs, reg_bits, m_second_program);
dest_mask = decode_write_mask(inst.opr.dest.bank, dest_mask, move_data_type == DataType::F16);
decoded_dest_mask = decode_write_mask(decoded_inst.opr.dest.bank, dest_mask, move_data_type == DataType::F16);
// Velocity uses a vec4 table, non-extended, so i assumes type=vec4, extended=false
inst.opr.src1.swizzle = decode_vec34_swizzle(src0_swiz, false, 2);
decoded_inst.opr.src1.swizzle = decode_vec34_swizzle(src0_swiz, false, 2);
inst.opr.src1.type = move_data_type;
inst.opr.dest.type = move_data_type;
// TODO: adjust dest mask if needed
CompareMethod compare_method = CompareMethod::NE_ZERO;
spv::Op compare_op = spv::OpAny;
decoded_inst.opr.src1.type = move_data_type;
decoded_inst.opr.dest.type = move_data_type;
if (is_conditional) {
inst.opr.src0.type = is_u8_conditional ? DataType::UINT8 : DataType::F32;
compare_method = static_cast<CompareMethod>((test_bit_2 << 1) | test_bit_1);
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank_sel, end_or_src0_bank_ext, is_double_regs, reg_bits, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank_sel, src2_bank_ext, is_double_regs, reg_bits, m_second_program);
decoded_inst.opr.src0.type = is_u8_conditional ? DataType::UINT8 : DataType::F32;
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank_sel, end_or_src0_bank_ext, is_double_regs, reg_bits, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank_sel, src2_bank_ext, is_double_regs, reg_bits, m_second_program);
if (src0_comp_sel) {
inst.opr.src0.swizzle = inst.opr.src1.swizzle;
decoded_inst.opr.src0.swizzle = decoded_inst.opr.src1.swizzle;
}
inst.opr.src2.swizzle = inst.opr.src1.swizzle;
switch (compare_method) {
case CompareMethod::LT_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpULessThan;
else
compare_op = spv::Op::OpFOrdLessThan;
break;
case CompareMethod::LTE_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpULessThanEqual;
else
compare_op = spv::Op::OpFOrdLessThanEqual;
break;
case CompareMethod::NE_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpINotEqual;
else
compare_op = spv::Op::OpFOrdNotEqual;
break;
case CompareMethod::EQ_ZERO:
if (is_u8_conditional)
compare_op = spv::Op::OpIEqual;
else
compare_op = spv::Op::OpFOrdEqual;
break;
}
decoded_inst.opr.src2.swizzle = decoded_inst.opr.src1.swizzle;
decoded_inst.opr.src2.type = move_data_type;
}
if (inst.opr.dest.bank == RegisterBank::SPECIAL || inst.opr.src0.bank == RegisterBank::SPECIAL || inst.opr.src1.bank == RegisterBank::SPECIAL || inst.opr.src2.bank == RegisterBank::SPECIAL) {
if (decoded_inst.opr.dest.bank == RegisterBank::SPECIAL || decoded_inst.opr.src0.bank == RegisterBank::SPECIAL || decoded_inst.opr.src1.bank == RegisterBank::SPECIAL || decoded_inst.opr.src2.bank == RegisterBank::SPECIAL) {
LOG_WARN("Special regs unsupported");
return false;
}
// Recompile
m_b.setLine(m_recompiler.cur_pc);
if ((move_data_type == DataType::F16) || (move_data_type == DataType::F32)) {
set_repeat_multiplier(2, 2, 2, 2);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
CompareMethod compare_method = CompareMethod::NE_ZERO;
if (is_conditional) {
compare_method = static_cast<CompareMethod>((test_bit_2 << 1) | test_bit_1);
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(inst, RepeatMode::SLMSI);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
std::string conditional_str;
if (is_conditional) {
@@ -166,98 +127,14 @@ bool USSETranslatorVisitor::vmov(
expr = "!=";
break;
}
conditional_str = fmt::format(" ({} {} vec(0)) ?", disasm::operand_to_str(inst.opr.src0, dest_mask), expr);
conditional_str = fmt::format(" ({} {} vec(0)) ?", disasm::operand_to_str(decoded_inst.opr.src0, decoded_dest_mask), expr);
}
const std::string disasm_str = fmt::format("{:016x}: {}{}.{} {}{} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), disasm::data_type_str(move_data_type),
disasm::operand_to_str(inst.opr.dest, dest_mask, dest_repeat_offset), conditional_str, disasm::operand_to_str(inst.opr.src1, dest_mask, src1_repeat_offset),
is_conditional ? fmt::format(": {}", disasm::operand_to_str(inst.opr.src2, dest_mask, src2_repeat_offset)) : "");
const std::string disasm_str = fmt::format("{:016x}: {}{}.{} {}{} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(decoded_inst.opcode), disasm::data_type_str(move_data_type),
disasm::operand_to_str(decoded_inst.opr.dest, decoded_dest_mask, dest_repeat_offset), conditional_str, disasm::operand_to_str(decoded_inst.opr.src1, decoded_dest_mask, src1_repeat_offset),
is_conditional ? fmt::format(": {}", disasm::operand_to_str(decoded_inst.opr.src2, decoded_dest_mask, src2_repeat_offset)) : "");
LOG_DISASM(disasm_str);
spv::Id source_to_compare_with_0 = spv::NoResult;
spv::Id source_1 = load(inst.opr.src1, dest_mask, src1_repeat_offset);
spv::Id source_2 = spv::NoResult;
spv::Id result = spv::NoResult;
if (source_1 == spv::NoResult) {
LOG_ERROR("Source not Loaded");
return false;
}
if (is_conditional) {
source_to_compare_with_0 = load(inst.opr.src0, dest_mask, src0_repeat_offset);
source_2 = load(inst.opr.src2, dest_mask, src2_repeat_offset);
spv::Id result_type = m_b.getTypeId(source_2);
spv::Id v0_comp_type = is_u8_conditional ? m_b.makeUintType(32) : m_b.makeFloatType(32);
spv::Id v0_type = utils::make_vector_or_scalar_type(m_b, v0_comp_type, m_b.getNumComponents(source_2));
spv::Id v0 = utils::make_uniform_vector_from_type(m_b, v0_type, 0);
bool source_2_first = false;
if (compare_op != spv::OpAny) {
// Merely do what the instruction does
// First compare source0 with vector 0
spv::Id cond_result = m_b.createOp(compare_op, m_b.makeVectorType(m_b.makeBoolType(), m_b.getNumComponents(source_to_compare_with_0)),
{ source_to_compare_with_0, v0 });
// For each component, if the compare result is true, move the equivalent component from source1 to dest,
// else the same thing with source2
// This behavior matches with OpSelect, so use it. Since IMix doesn't exist (really)
result = m_b.createOp(spv::OpSelect, result_type, { cond_result, source_1, source_2 });
} else {
// We optimize the float case. We can make the GPU use native float instructions without touching bool or integers
// Taking advantage of the mix function: if we use absolute 0 and 1 as the lerp, we got the equivalent of:
// mix(a, b, c) with c.comp is either 0 or 1 <=> if c.comp == 0 return a else return b.
switch (compare_method) {
case CompareMethod::LT_ZERO: {
// For each component: if source0.comp < 0 return 0 else return 1
// That means if we use mix, it should be mix(src1, src2, step_result)
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450Step, { v0, source_to_compare_with_0 });
source_2_first = false;
break;
}
case CompareMethod::LTE_ZERO: {
// For each component: if 0 < source0.comp return 0 else return 1
// Or, if we turn it around: if source0.comp <= 0 return 1 else return 0
// That means if we use mix, it should be mix(src2, src1, step_result)
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450Step, { source_to_compare_with_0, v0 });
source_2_first = true;
break;
}
case CompareMethod::NE_ZERO:
case CompareMethod::EQ_ZERO: {
// Taking advantage of the sign and absolute instruction
// The sign instruction returns 0 if the component equals to 0, else 1 if positive, -1 if negative
// That means if we absolute the sign result, we got 0 if component equals to 0, else we got 1.
// src2 will be first for Not equal case.
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FSign, { source_to_compare_with_0 });
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FAbs, { result });
if (compare_method == CompareMethod::NE_ZERO) {
source_2_first = true;
}
break;
}
default: {
LOG_ERROR("Unknown compare method: {}", static_cast<int>(compare_method));
return false;
}
}
// Mixing!! I'm like a little witch!!
result = m_b.createBuiltinCall(result_type, std_builtins, GLSLstd450FMix, { source_2_first ? source_2 : source_1, source_2_first ? source_1 : source_2, result });
}
} else {
result = source_1;
}
store(inst.opr.dest, result, dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
@@ -290,8 +167,6 @@ bool USSETranslatorVisitor::vpck(
Imm1 comp0_sel_bit1,
Imm6 src2_n,
Imm1 comp_sel_0_bit0) {
Instruction inst;
// TODO: There are some combinations that are invalid.
const DataType dest_data_type_table[] = {
DataType::UINT8,
@@ -382,58 +257,63 @@ bool USSETranslatorVisitor::vpck(
Opcode::VPCKC10C10 }
};
inst.opcode = op_table[dest_fmt][src_fmt];
decoded_inst.opcode = op_table[dest_fmt][src_fmt];
std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode));
std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(decoded_inst.opcode));
inst.opr.dest.type = dest_data_type_table[dest_fmt];
inst.opr.src1.type = src_data_type_table[src_fmt];
decoded_inst.opr.dest.type = dest_data_type_table[dest_fmt];
decoded_inst.opr.src1.type = src_data_type_table[src_fmt];
decoded_inst.opr.src2.type = decoded_inst.opr.src1.type;
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank_sel, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank_sel, dest_bank_ext, false, 7, m_second_program);
bool should_src1_dreg = true;
if (!is_float_data_type(inst.opr.src1.type)) {
if (!is_float_data_type(decoded_inst.opr.src1.type)) {
// For non-float source,
src1_n = comp0_sel_bit1 | (src1_n << 1);
should_src1_dreg = false;
}
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank_sel, src1_bank_ext, should_src1_dreg, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank_sel, src2_bank_ext, true, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank_sel, src1_bank_ext, should_src1_dreg, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank_sel, src2_bank_ext, true, 7, m_second_program);
if (inst.opr.dest.bank == RegisterBank::SPECIAL || inst.opr.src0.bank == RegisterBank::SPECIAL || inst.opr.src1.bank == RegisterBank::SPECIAL || inst.opr.src2.bank == RegisterBank::SPECIAL) {
if (decoded_inst.opr.dest.bank == RegisterBank::SPECIAL || decoded_inst.opr.src0.bank == RegisterBank::SPECIAL || decoded_inst.opr.src1.bank == RegisterBank::SPECIAL || decoded_inst.opr.src2.bank == RegisterBank::SPECIAL) {
LOG_WARN("Special regs unsupported");
return false;
}
Imm2 comp_sel_0 = comp_sel_0_bit0;
if (inst.opr.src1.type == DataType::F32)
if (decoded_inst.opr.src1.type == DataType::F32)
comp_sel_0 |= (comp0_sel_bit1 & 1) << 1;
else
comp_sel_0 |= (src2_n & 1) << 1;
inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_CAST(comp_sel_0, comp_sel_1, comp_sel_2, comp_sel_3);
decoded_inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_CAST(comp_sel_0, comp_sel_1, comp_sel_2, comp_sel_3);
// For some occasions the swizzle needs to cycle from the first components to the first bit that was on in the dest mask.
bool no_swizzle_cycle_to_mask = (is_float_data_type(inst.opr.src1.type) && is_float_data_type(inst.opr.dest.type))
|| (scale && ((inst.opr.src1.type == DataType::UINT8) || (inst.opr.dest.type == DataType::UINT8)));
bool no_swizzle_cycle_to_mask = (is_float_data_type(decoded_inst.opr.src1.type) && is_float_data_type(decoded_inst.opr.dest.type))
|| (scale && ((decoded_inst.opr.src1.type == DataType::UINT8) || (decoded_inst.opr.dest.type == DataType::UINT8)));
bool should_use_src2 = false;
constexpr Imm4 CONTIGUOUS_MASKS[] = { 0b1, 0b11, 0b111, 0b1111 };
if (inst.opr.src1.type == DataType::F32 && inst.opr.src2.bank != RegisterBank::IMMEDIATE) {
const bool is_two_source_same = inst.opr.src1.num == inst.opr.src2.num && inst.opr.src1.bank == inst.opr.src2.bank;
if (decoded_inst.opr.src1.type == DataType::F32 && decoded_inst.opr.src2.bank != RegisterBank::IMMEDIATE) {
const bool is_two_source_same = decoded_inst.opr.src1.num == decoded_inst.opr.src2.num && decoded_inst.opr.src1.bank == decoded_inst.opr.src2.bank;
const bool is_contiguous = std::any_of(std::begin(CONTIGUOUS_MASKS), std::end(CONTIGUOUS_MASKS), [&dest_mask](const auto mask) { return dest_mask == mask; });
if (!is_default(inst.opr.src1.swizzle, dest_mask_to_comp_count(dest_mask)) || !is_two_source_same || !is_contiguous) {
if (!is_default(decoded_inst.opr.src1.swizzle, dest_mask_to_comp_count(dest_mask)) || !is_two_source_same || !is_contiguous) {
should_use_src2 = true;
} else {
decoded_inst.opr.src2.type = DataType::UNK;
}
} else {
decoded_inst.opr.src2.type = DataType::UNK;
}
if (!no_swizzle_cycle_to_mask) {
shader::usse::Swizzle4 swizz_temp = inst.opr.src1.swizzle;
const bool check_only_two_swizz = (inst.opr.src1.type == DataType::UINT8) || (inst.opr.dest.type == DataType::UINT8);
shader::usse::Swizzle4 swizz_temp = decoded_inst.opr.src1.swizzle;
const bool check_only_two_swizz = (decoded_inst.opr.src1.type == DataType::UINT8) || (decoded_inst.opr.dest.type == DataType::UINT8);
int swizz_src_taken = 0;
@@ -441,26 +321,22 @@ bool USSETranslatorVisitor::vpck(
for (int i = 0; i < 4; i++) {
if (dest_mask & (1 << i)) {
if (check_only_two_swizz && (dest_mask == 0b1111)) {
inst.opr.src1.swizzle[i] = swizz_temp[i % 2];
decoded_inst.opr.src1.swizzle[i] = swizz_temp[i % 2];
} else {
inst.opr.src1.swizzle[i] = swizz_temp[swizz_src_taken++];
decoded_inst.opr.src1.swizzle[i] = swizz_temp[swizz_src_taken++];
}
}
}
}
// Recompile
m_b.setLine(m_recompiler.cur_pc);
// Doing this extra dest type check for future change in case I'm wrong (pent0)
if (is_integer_data_type(inst.opr.dest.type)) {
if (is_float_data_type(inst.opr.src1.type)) {
if (is_integer_data_type(decoded_inst.opr.dest.type)) {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
}
} else {
if (is_float_data_type(inst.opr.src1.type)) {
if (is_float_data_type(decoded_inst.opr.src1.type)) {
set_repeat_multiplier(1, 2, 2, 1);
} else {
set_repeat_multiplier(1, 1, 1, 1);
@@ -468,48 +344,19 @@ bool USSETranslatorVisitor::vpck(
}
BEGIN_REPEAT(repeat_count)
GET_REPEAT(inst, RepeatMode::SLMSI);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
if (should_use_src2) {
// TODO correctly log
LOG_DISASM("{} {} ({} {}) [{}]", disasm_str, disasm::operand_to_str(inst.opr.dest, dest_mask, dest_repeat_offset),
disasm::operand_to_str(inst.opr.src1, dest_mask, src1_repeat_offset),
disasm::operand_to_str(inst.opr.src2, 0b1111, src2_repeat_offset), scale ? "scale" : "noscale");
LOG_DISASM("{} {} ({} {}) [{}]", disasm_str, disasm::operand_to_str(decoded_inst.opr.dest, dest_mask, dest_repeat_offset),
disasm::operand_to_str(decoded_inst.opr.src1, dest_mask, src1_repeat_offset),
disasm::operand_to_str(decoded_inst.opr.src2, 0b1111, src2_repeat_offset), scale ? "scale" : "noscale");
} else {
LOG_DISASM("{} {} {} [{}]", disasm_str, disasm::operand_to_str(inst.opr.dest, dest_mask, dest_repeat_offset),
disasm::operand_to_str(inst.opr.src1, dest_mask, src1_repeat_offset), scale ? "scale" : "noscale");
LOG_DISASM("{} {} {} [{}]", disasm_str, disasm::operand_to_str(decoded_inst.opr.dest, dest_mask, dest_repeat_offset),
disasm::operand_to_str(decoded_inst.opr.src1, dest_mask, src1_repeat_offset), scale ? "scale" : "noscale");
}
spv::Id source = load(inst.opr.src1, dest_mask, src1_repeat_offset);
if (source == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
if (should_use_src2) {
Operand src1 = inst.opr.src1;
Operand src2 = inst.opr.src2;
src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
src2.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id source1 = load(src1, 0b11, src1_repeat_offset);
spv::Id source2 = load(src2, 0b11, src2_repeat_offset);
source = utils::finalize(m_b, source1, source2, inst.opr.src1.swizzle, 0, dest_mask);
}
// source is int destination is float
if (is_float_data_type(inst.opr.dest.type) && !is_float_data_type(inst.opr.src1.type)) {
source = utils::convert_to_float(m_b, source, inst.opr.src1.type, scale);
}
// source is float destination is int
if (!is_float_data_type(inst.opr.dest.type) && is_float_data_type(inst.opr.src1.type)) {
source = utils::convert_to_int(m_b, source, inst.opr.dest.type, scale);
}
store(inst.opr.dest, source, dest_mask, dest_repeat_offset);
END_REPEAT()
reset_repeat_multiplier();
return true;
@@ -545,10 +392,7 @@ bool USSETranslatorVisitor::vldst(
// TODO:
// - Store instruction
// - Post or pre or any increment mode.
Instruction inst;
inst.opcode = Opcode::LDR;
decoded_inst.opcode = Opcode::LDR;
DataType type_to_ldst = DataType::UNK;
switch (data_type) {
@@ -571,97 +415,32 @@ bool USSETranslatorVisitor::vldst(
const int total_number_to_fetch = mask_count + 1;
const int total_bytes_fo_fetch = get_data_type_size(type_to_ldst) * total_number_to_fetch;
Operand to_store;
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank, src0_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src0.type = DataType::INT32;
decoded_inst.opr.src1.type = DataType::INT32;
decoded_inst.opr.src2.type = DataType::INT32;
if (is_translating_secondary_program()) {
to_store.bank = RegisterBank::SECATTR;
decoded_inst.opr.dest.bank = RegisterBank::SECATTR;
} else {
if (dest_bank_primattr) {
to_store.bank = RegisterBank::PRIMATTR;
decoded_inst.opr.dest.bank = RegisterBank::PRIMATTR;
} else {
to_store.bank = RegisterBank::TEMP;
decoded_inst.opr.dest.bank = RegisterBank::TEMP;
}
}
to_store.num = dest_n;
to_store.type = DataType::F32;
decoded_inst.opr.dest.num = dest_n;
decoded_inst.opr.dest.type = DataType::F32;
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank, src0_bank_ext, false, 7, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
inst.opr.src0.type = DataType::INT32;
inst.opr.src1.type = DataType::INT32;
inst.opr.src2.type = DataType::INT32;
std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode));
LOG_DISASM("{} {} ({} + {} + {}) [{} bytes]", disasm_str, disasm::operand_to_str(to_store, 0b1, 0),
disasm::operand_to_str(inst.opr.src0, 0b1, 0),
disasm::operand_to_str(inst.opr.src1, 0b1, 0), disasm::operand_to_str(inst.opr.src2, 0b1, 0), total_bytes_fo_fetch);
// TODO: is source_2 in word or byte? Is it even used at all?
spv::Id source_0 = load(inst.opr.src0, 0b1, 0);
if (inst.opr.src1.bank == RegisterBank::IMMEDIATE) {
inst.opr.src1.num *= get_data_type_size(type_to_ldst);
}
spv::Id source_1 = load(inst.opr.src1, 0b1, 0);
spv::Id source_2 = load(inst.opr.src2, 0b1, 0);
// Seems that if it's indexed by register, offset is in bytes and based on 0x10000?
// Maybe that's just how the memory map operates. I'm not sure. However the literals on all shader so far is that
// Another thing is that, when moe expand is not enable, there seems to be 4 bytes added before fetching... No absolute prove.
// Maybe moe expand means it's not fetching after all? Dunno
std::uint32_t REG_INDEX_BASE = 0x10000;
spv::Id reg_index_base_cst = m_b.makeIntConstant(REG_INDEX_BASE);
if (inst.opr.src1.bank != shader::usse::RegisterBank::IMMEDIATE) {
source_1 = m_b.createBinOp(spv::OpISub, m_b.getTypeId(source_1), source_1, reg_index_base_cst);
}
spv::Id i32_type = m_b.makeIntType(32);
spv::Id base = m_b.createBinOp(spv::OpIAdd, i32_type, source_0, source_1);
base = m_b.createBinOp(spv::OpIAdd, i32_type, base, source_2);
if (!moe_expand) {
base = m_b.createBinOp(spv::OpIAdd, i32_type, base, m_b.makeIntConstant(4));
}
for (int i = 0; i < total_bytes_fo_fetch / 4; ++i) {
spv::Id offset = m_b.createBinOp(spv::OpIAdd, m_b.makeIntType(32), base, m_b.makeIntConstant(4 * i));
spv::Id src = utils::fetch_memory(m_b, m_spirv_params, m_util_funcs, offset);
store(to_store, src, 0b1);
to_store.num += 1;
}
std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(decoded_inst.opcode));
LOG_DISASM("{} {} ({} + {} + {}) [{} bytes]", disasm_str, disasm::operand_to_str(decoded_inst.opr.dest, 0b1, 0),
disasm::operand_to_str(decoded_inst.opr.src0, 0b1, 0),
disasm::operand_to_str(decoded_inst.opr.src1, 0b1, 0), disasm::operand_to_str(decoded_inst.opr.src2, 0b1, 0), total_bytes_fo_fetch);
return true;
}
bool USSETranslatorVisitor::limm(
bool skipinv,
bool nosched,
bool dest_bank_ext,
bool end,
Imm6 imm_value_bits26to31,
ExtPredicate pred,
Imm5 imm_value_bits21to25,
Imm2 dest_bank,
Imm7 dest_num,
Imm21 imm_value_first_21bits) {
Instruction inst;
inst.opcode = Opcode::MOV;
std::uint32_t imm_value = imm_value_first_21bits | (imm_value_bits21to25 << 21) | (imm_value_bits26to31 << 26);
spv::Id const_imm_id = m_b.makeUintConstant(imm_value);
inst.dest_mask = 0b1;
inst.opr.dest = decode_dest(inst.opr.dest, dest_num, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.dest.type = DataType::UINT32;
const std::string disasm_str = fmt::format("{:016x}: {}{}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode));
LOG_DISASM("{} {} #0x{:X}", disasm_str, disasm::operand_to_str(inst.opr.dest, 0b1, 0), imm_value);
store(inst.opr.dest, const_imm_id, 0b1);
return true;
}
} // namespace shader::usse
+94 -252
View File
@@ -1,5 +1,6 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,19 +16,13 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/translator.h>
#include <shader/types.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
namespace shader::usse {
bool USSETranslatorVisitor::vbw(
Imm3 op1,
ExtPredicate pred,
@@ -52,99 +47,39 @@ bool USSETranslatorVisitor::vbw(
Imm7 src2_sel,
Imm7 src1_n,
Imm7 src2_n) {
Instruction inst;
switch (op1) {
case 0b010: inst.opcode = op2 ? Opcode::OR : Opcode::AND; break;
case 0b011: inst.opcode = Opcode::XOR; break;
case 0b100: inst.opcode = op2 ? Opcode::ROL : Opcode::SHL; break;
case 0b101: inst.opcode = op2 ? Opcode::ASR : Opcode::SHR; break;
case 0b010: decoded_inst.opcode = op2 ? Opcode::OR : Opcode::AND; break;
case 0b011: decoded_inst.opcode = Opcode::XOR; break;
case 0b100: decoded_inst.opcode = op2 ? Opcode::ROL : Opcode::SHL; break;
case 0b101: decoded_inst.opcode = op2 ? Opcode::ASR : Opcode::SHR; break;
default: return false;
}
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_ext, false, 7, m_second_program);
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank, dest_ext, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank, dest_ext, false, 7, m_second_program);
DataType type = bitwise_partial ? DataType::UINT16 : DataType::UINT32;
inst.opr.src1.type = type;
inst.opr.src2.type = type;
inst.opr.dest.type = type;
decoded_inst.opr.src1.type = DataType::UINT32;
decoded_inst.opr.src2.type = DataType::UINT32;
decoded_inst.opr.dest.type = DataType::UINT32;
set_repeat_multiplier(1, 1, 1, 1);
BEGIN_REPEAT(repeat_count)
GET_REPEAT(inst, RepeatMode::SLMSI);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
bool immediate = src2_ext && inst.opr.src2.bank == RegisterBank::IMMEDIATE;
uint32_t value = 0;
if (src2_rot) {
LOG_WARN("Bitwise Rotations are unsupported.");
return false;
}
spv::Id src2 = 0;
bool immediate = src2_ext && decoded_inst.opr.src2.bank == RegisterBank::IMMEDIATE;
std::uint32_t value = 0;
if (immediate) {
value = src2_n | (static_cast<uint32_t>(src2_sel) << 7) | (static_cast<uint32_t>(src2_exth) << 14);
src2 = m_b.makeUintConstant(src2_invert ? ~value : value);
} else {
src2 = load(inst.opr.src2, 0b0001, src2_repeat_offset);
if (src2 == spv::NoResult) {
LOG_ERROR("Source 2 not loaded");
return false;
}
if (src2_invert) {
src2 = m_b.createUnaryOp(spv::Op::OpNot, type_ui32, src2);
value = ~value;
}
}
spv::Id result;
spv::Op operation;
switch (inst.opcode) {
case Opcode::OR: operation = spv::Op::OpBitwiseOr; break;
case Opcode::AND: operation = spv::Op::OpBitwiseAnd; break;
case Opcode::XOR: operation = spv::Op::OpBitwiseXor; break;
case Opcode::ROL:
LOG_WARN("Bitwise Rotate Left operation unsupported.");
return false; // TODO: SPIRV doesn't seem to have a rotate left operation!
case Opcode::ASR: operation = spv::Op::OpShiftRightArithmetic; break;
case Opcode::SHL: operation = spv::Op::OpShiftLeftLogical; break;
case Opcode::SHR: operation = spv::Op::OpShiftRightLogical; break;
default: return false;
}
// optimisation. (any OR 0 || any XOR 0 || any AND 0xFFFFFFFF) -> assign
bool is_const = m_b.getOpCode(src2) == spv::Op::OpConstant;
auto const_val = is_const ? m_b.getConstantScalar(src2) : 1; // default value is intentionally non zero
if ((operation == spv::Op::OpBitwiseOr || operation == spv::Op::OpBitwiseXor) && is_const && const_val == 0
|| operation == spv::Op::OpBitwiseAnd && is_const && const_val == std::numeric_limits<decltype(const_val)>::max()) {
inst.opr.src1.type = bitwise_partial ? DataType::F16 : DataType::F32;
inst.opr.dest.type = bitwise_partial ? DataType::F16 : DataType::F32;
result = load(inst.opr.src1, 0b0001, src1_repeat_offset);
if (result == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
} else {
spv::Id src1 = load(inst.opr.src1, 0b0001, src1_repeat_offset);
if (src1 == spv::NoResult) {
LOG_ERROR("Source not loaded");
return false;
}
result = m_b.createBinOp(operation, type_ui32, src1, src2);
if (m_b.isFloatType(m_b.getTypeId(src2))) {
result = m_b.createUnaryOp(spv::Op::OpBitcast, type_f32, src2);
}
}
store(inst.opr.dest, result, 0b0001, dest_repeat_offset);
LOG_DISASM("{:016x}: {}{}{} {} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(inst.opcode), bitwise_partial ? "16" : "",
disasm::operand_to_str(inst.opr.dest, 0b0001, dest_repeat_offset), disasm::operand_to_str(inst.opr.src1, 0b0001, src1_repeat_offset),
immediate ? log_hex(value) : disasm::operand_to_str(inst.opr.src2, 0b0001, src2_repeat_offset));
LOG_DISASM("{:016x}: {}{} {} {} {}", m_instr, disasm::e_predicate_str(pred), disasm::opcode_str(decoded_inst.opcode),
disasm::operand_to_str(decoded_inst.opr.dest, 0b0001, dest_repeat_offset), disasm::operand_to_str(decoded_inst.opr.src1, 0b0001, src1_repeat_offset),
immediate ? log_hex(value) : disasm::operand_to_str(decoded_inst.opr.src2, 0b0001, src2_repeat_offset));
END_REPEAT()
@@ -182,19 +117,17 @@ bool USSETranslatorVisitor::i8mad(
Imm7 src0_num,
Imm7 src1_num,
Imm7 src2_num) {
Instruction inst;
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_num, src0_bank, false, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_num, src1_bank, src1_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_num, src2_bank, src2_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_num, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.src0 = decode_src0(inst.opr.src0, src0_num, src0_bank, false, false, 7, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_num, src1_bank, src1_bank_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_num, src2_bank, src2_bank_ext, false, 7, m_second_program);
inst.opr.dest = decode_dest(inst.opr.dest, dest_num, dest_bank, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src0.type = DataType::UINT8;
decoded_inst.opr.src1.type = DataType::UINT8;
decoded_inst.opr.src2.type = DataType::UINT8;
decoded_inst.opr.dest.type = DataType::UINT8;
inst.opr.src0.type = DataType::UINT8;
inst.opr.src1.type = DataType::UINT8;
inst.opr.src2.type = DataType::UINT8;
inst.opr.dest.type = DataType::UINT8;
inst.opcode = Opcode::IMA8;
decoded_inst.opcode = Opcode::IMA8;
usse::Swizzle4 src1_swizz = SWIZZLE_CHANNEL_4_DEFAULT;
usse::Swizzle4 src2_swizz = SWIZZLE_CHANNEL_4_DEFAULT;
@@ -213,98 +146,51 @@ bool USSETranslatorVisitor::i8mad(
src2_swizz[2] = usse::SwizzleChannel::C_W;
}
inst.opr.src1.swizzle = src1_swizz;
inst.opr.src2.swizzle = src2_swizz;
decoded_inst.opr.src1.swizzle = src1_swizz;
decoded_inst.opr.src2.swizzle = src2_swizz;
if ((amod0) || (amod1) || (amod2) || (cmod0) || (cmod1) || (cmod2)) {
LOG_ERROR("Custom modifiers for components not handled!");
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(inst, RepeatMode::SLMSI);
inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id src1_mul = load(inst.opr.src1, 0b1111, src1_repeat_offset);
spv::Id src2_mul = load(inst.opr.src2, 0b1111, src2_repeat_offset);
spv::Id src0_add = load(inst.opr.src0, 0b1111, src0_repeat_offset);
spv::Id final_add = src0_add;
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
usse::Swizzle3 add_swizz_rgb = SWIZZLE_CHANNEL_3_DEFAULT;
bool add_swizz_rgb_src0 = true;
if ((csel0 != 0) || (asel0 != 0)) {
// We build source0 (the add component in this loop)
// Using OpVectorShuffle to construct the final one.
std::vector<spv::Id> shuffle_ops = { src0_add, src1_mul };
switch (csel0) {
case 0:
shuffle_ops.insert(shuffle_ops.end(), { 0, 1, 2 });
break;
case 1:
// Use src1 rgb
shuffle_ops.insert(shuffle_ops.end(), { 4, 5, 6 });
add_swizz_rgb_src0 = false;
break;
case 2:
// Use src0 full alpha
shuffle_ops.insert(shuffle_ops.end(), { 3, 3, 3 });
add_swizz_rgb = { usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W };
break;
case 3:
// Use src1 full alpha
shuffle_ops.insert(shuffle_ops.end(), { 7, 7, 7 });
add_swizz_rgb = { usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W, usse::SwizzleChannel::C_W };
add_swizz_rgb_src0 = false;
break;
default:
assert(false);
break;
}
switch (asel0) {
case 0:
// Use src0 alpha
shuffle_ops.push_back(3);
break;
case 1:
// Use src1 alpha
shuffle_ops.push_back(7);
break;
default:
assert(false);
break;
}
final_add = m_b.createOp(spv::OpVectorShuffle, m_b.getTypeId(src0_add), shuffle_ops);
}
spv::Id result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(src1_mul), src1_mul, src2_mul);
result = m_b.createBinOp(src0_neg ? spv::OpISub : spv::OpIAdd, m_b.getTypeId(src1_mul), result, src0_add);
store(inst.opr.dest, result, 0b1111, dest_repeat_offset);
inst.opr.src0.swizzle[0] = add_swizz_rgb[0];
inst.opr.src0.swizzle[1] = add_swizz_rgb[1];
inst.opr.src0.swizzle[2] = add_swizz_rgb[2];
decoded_inst.opr.src0.swizzle[0] = add_swizz_rgb[0];
decoded_inst.opr.src0.swizzle[1] = add_swizz_rgb[1];
decoded_inst.opr.src0.swizzle[2] = add_swizz_rgb[2];
LOG_DISASM("{:016x}: {}{} {} {} {} {}({}, {})", m_instr, disasm::s_predicate_str(static_cast<usse::ShortPredicate>(pred)),
disasm::opcode_str(inst.opcode), disasm::operand_to_str(inst.opr.dest, 0b1111, dest_repeat_offset),
disasm::operand_to_str(inst.opr.src1, 0b1111, src1_repeat_offset),
disasm::operand_to_str(inst.opr.src2, 0b1111, src2_repeat_offset),
src0_neg ? "-" : "", disasm::operand_to_str((add_swizz_rgb_src0 ? inst.opr.src0 : inst.opr.src1), 0b0111, src0_repeat_offset),
disasm::operand_to_str(asel0 ? inst.opr.src1 : inst.opr.src0, 0b1000, src0_repeat_offset));
disasm::opcode_str(decoded_inst.opcode), disasm::operand_to_str(decoded_inst.opr.dest, 0b1111, dest_repeat_offset),
disasm::operand_to_str(decoded_inst.opr.src1, 0b1111, src1_repeat_offset),
disasm::operand_to_str(decoded_inst.opr.src2, 0b1111, src2_repeat_offset),
src0_neg ? "-" : "", disasm::operand_to_str((add_swizz_rgb_src0 ? decoded_inst.opr.src0 : decoded_inst.opr.src1), 0b0111, src0_repeat_offset),
disasm::operand_to_str(asel0 ? decoded_inst.opr.src1 : decoded_inst.opr.src0, 0b1000, src0_repeat_offset));
END_REPEAT();
@@ -345,63 +231,48 @@ bool USSETranslatorVisitor::i16mad(
LOG_DISASM("Saturation in i16mad not yet supported!");
}
Instruction inst;
inst.opcode = Opcode::IMA16;
decoded_inst.opcode = Opcode::IMA16;
DataType operate_type = ((mode == 0) || (mode == 1)) ? DataType::UINT16 : DataType::INT16;
bool is_signed = (mode >= 2);
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank, false, false, 7, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank, false, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
inst.opr.dest.type = inst.opr.src0.type = inst.opr.src1.type = inst.opr.src2.type = operate_type;
std::uint8_t mask_src1 = 0b1;
std::uint8_t mask_src2 = 0b1;
decoded_inst.opr.dest.type = decoded_inst.opr.src0.type = decoded_inst.opr.src1.type = decoded_inst.opr.src2.type = operate_type;
decoded_source_mask = 0b1;
decoded_source_mask_2 = 0b1;
if (src1_format != 0) {
if (src1_format == 1) {
inst.opr.src1.type = (is_signed ? DataType::INT8 : DataType::UINT8);
decoded_inst.opr.src1.type = (is_signed ? DataType::INT8 : DataType::UINT8);
} else {
LOG_DISASM("Only support selecting non sign-extended 8-bit integer for SRC1!");
}
if (sel1h_upper8) {
mask_src1 = 0b10;
decoded_source_mask = 0b10;
}
}
if (src2_format != 0) {
if (src2_format == 1) {
inst.opr.src2.type = (is_signed ? DataType::INT8 : DataType::UINT8);
decoded_inst.opr.src2.type = (is_signed ? DataType::INT8 : DataType::UINT8);
} else {
LOG_DISASM("Only support selecting non sign-extended 8-bit integer for SRC2!");
}
if (sel2h_upper8) {
mask_src2 = 0b10;
decoded_source_mask_2 = 0b10;
}
}
BEGIN_REPEAT(repeat_count);
GET_REPEAT(inst, RepeatMode::SLMSI);
GET_REPEAT(decoded_inst, RepeatMode::SLMSI);
LOG_DISASM("{:016x}: {}{} {} {} {} {}", m_instr, disasm::s_predicate_str(pred), "IMAD16", disasm::operand_to_str(inst.opr.dest, 0b1),
disasm::operand_to_str(inst.opr.src0, 0b1), disasm::operand_to_str(inst.opr.src1, mask_src1) + ((src1_format != 0) ? "-8bits" : ""),
disasm::operand_to_str(inst.opr.src2, mask_src2) + ((src2_format != 0) ? "-8bits" : ""));
inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
spv::Id source0 = load(inst.opr.src0, 0b1, src0_repeat_offset);
spv::Id source1 = load(inst.opr.src1, mask_src1, src1_repeat_offset);
spv::Id source2 = load(inst.opr.src2, mask_src2, src2_repeat_offset);
spv::Id source0_type = m_b.getTypeId(source0);
auto mul_result = m_b.createBinOp(spv::OpIMul, source0_type, source0, source1);
auto add_result = m_b.createBinOp(spv::OpIAdd, source0_type, mul_result, source2);
if (add_result != spv::NoResult) {
store(inst.opr.dest, add_result, 0b1, dest_repeat_offset);
}
LOG_DISASM("{:016x}: {}{} {} {} {} {}", m_instr, disasm::s_predicate_str(pred), "IMAD16", disasm::operand_to_str(decoded_inst.opr.dest, 0b11),
disasm::operand_to_str(decoded_inst.opr.src0, 0b11), disasm::operand_to_str(decoded_inst.opr.src1, decoded_source_mask) + ((src1_format != 0) ? "-8bits" : ""),
disasm::operand_to_str(decoded_inst.opr.src2, decoded_source_mask_2) + ((src2_format != 0) ? "-8bits" : ""));
END_REPEAT();
@@ -432,56 +303,46 @@ bool USSETranslatorVisitor::i32mad(
Imm7 src2_n) {
// TODO: high and low modifier
// TODO: more data types for src2
Instruction inst;
inst.opcode = Opcode::IMAD;
decoded_inst.opcode = Opcode::IMAD;
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank, false, false, 7, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank, false, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
const DataType inst_dt = is_signed ? DataType::INT32 : DataType::UINT32;
const DataType inst_dt_16 = is_signed ? DataType::INT16 : DataType::UINT16;
shader::usse::Imm4 src0_mask = 0b1;
shader::usse::Imm4 src1_mask = 0b1;
shader::usse::Imm4 src2_mask = 0b1;
decoded_source_mask = 0b1;
decoded_source_mask_2 = 0b1;
decoded_source_mask_3 = 0b1;
inst.opr.dest.type = inst_dt;
inst.opr.src0.type = inst_dt_16;
inst.opr.src1.type = inst_dt_16;
decoded_inst.opr.dest.type = inst_dt;
decoded_inst.opr.src0.type = inst_dt_16;
decoded_inst.opr.src1.type = inst_dt_16;
if (src2_type == 2) {
inst.opr.src2.type = inst_dt;
decoded_inst.opr.src2.type = inst_dt;
} else {
inst.opr.src2.type = inst_dt_16;
decoded_inst.opr.src2.type = inst_dt_16;
}
if (src0_high) {
src0_mask = 0b10;
decoded_source_mask = 0b10;
}
if (src1_high) {
src1_mask = 0b10;
decoded_source_mask_2 = 0b10;
}
if (src2_type != 2 && src2_high) {
src2_mask = 0b10;
decoded_source_mask_3 = 0b10;
}
spv::Id vsrc0 = load(inst.opr.src0, src0_mask, 0);
spv::Id vsrc1 = load(inst.opr.src1, src1_mask, 0);
spv::Id vsrc2 = load(inst.opr.src2, src2_mask, 0);
LOG_DISASM("{:016x}: {}{} {} {} {} {}", m_instr, disasm::s_predicate_str(pred), "IMAD2", disasm::operand_to_str(decoded_inst.opr.dest, 0b1),
disasm::operand_to_str(decoded_inst.opr.src0, decoded_source_mask), disasm::operand_to_str(decoded_inst.opr.src1, decoded_source_mask_2),
disasm::operand_to_str(decoded_inst.opr.src2, decoded_source_mask_3));
auto mul_result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(vsrc0), vsrc0, vsrc1);
auto add_result = m_b.createBinOp(spv::OpIAdd, m_b.getTypeId(mul_result), mul_result, vsrc2);
if (add_result != spv::NoResult) {
store(inst.opr.dest, add_result, 0b1, 0);
}
LOG_DISASM("{:016x}: {}{} {} {} {} {}", m_instr, disasm::s_predicate_str(pred), "IMAD2", disasm::operand_to_str(inst.opr.dest, 0b1),
disasm::operand_to_str(inst.opr.src0, 0b1), disasm::operand_to_str(inst.opr.src1, 0b1), disasm::operand_to_str(inst.opr.src2, 0b1));
return true;
}
@@ -506,50 +367,31 @@ bool USSETranslatorVisitor::i32mad2(
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
Instruction inst;
inst.opcode = Opcode::IMAD;
decoded_inst.opcode = Opcode::IMAD;
inst.opr.dest = decode_dest(inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank, src0_bank_ext, false, 7, m_second_program);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
decoded_inst.opr.dest = decode_dest(decoded_inst.opr.dest, dest_n, dest_bank, dest_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank, src0_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_bank_ext, false, 7, m_second_program);
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_bank_ext, false, 7, m_second_program);
const DataType inst_dt = is_signed ? DataType::INT32 : DataType::UINT32;
inst.opr.dest.type = inst_dt;
inst.opr.src0.type = inst_dt;
inst.opr.src1.type = inst_dt;
inst.opr.src2.type = inst_dt;
decoded_inst.opr.dest.type = inst_dt;
decoded_inst.opr.src0.type = inst_dt;
decoded_inst.opr.src1.type = inst_dt;
decoded_inst.opr.src2.type = inst_dt;
if (negative_src1) {
inst.opr.src1.flags |= RegisterFlags::Negative;
decoded_inst.opr.src1.flags |= RegisterFlags::Negative;
}
if (negative_src2) {
inst.opr.src2.flags |= RegisterFlags::Negative;
decoded_inst.opr.src2.flags |= RegisterFlags::Negative;
}
spv::Id vsrc0 = load(inst.opr.src0, 0b1, 0);
spv::Id vsrc1 = load(inst.opr.src1, 0b1, 0);
spv::Id vsrc2 = load(inst.opr.src2, 0b1, 0);
auto mul_result = m_b.createBinOp(spv::OpIMul, m_b.getTypeId(vsrc0), vsrc0, vsrc1);
auto add_result = m_b.createBinOp(spv::OpIAdd, m_b.getTypeId(mul_result), mul_result, vsrc2);
// sn is mysterious argument.
// These are confirmed by hw testing:
// - pa = x * y + z (sn = 0) => pa = x * y + z
// - i = x * y + z (sn = 0) and then pa = x * y + i (sn = 1) => pa = x * y + z
// - pa = x * y + z (sn = 1) => crash
// TODO: properly implement this when we get more powerful fuzzer that can handle fpinternal.
if (sn == 0) {
store(inst.opr.dest, add_result, 0b1, 0);
} else {
store(inst.opr.dest, vsrc2, 0b1, 0);
}
LOG_DISASM("{:016x}: {}{} {} {} {} {} [sn={}]", m_instr, disasm::e_predicate_str(pred), "IMAD", disasm::operand_to_str(inst.opr.dest, 0b1),
disasm::operand_to_str(inst.opr.src0, 0b1), disasm::operand_to_str(inst.opr.src1, 0b1), disasm::operand_to_str(inst.opr.src2, 0b1), sn);
LOG_DISASM("{:016x}: {}{} {} {} {} {} [sn={}]", m_instr, disasm::e_predicate_str(pred), "IMAD", disasm::operand_to_str(decoded_inst.opr.dest, 0b1),
disasm::operand_to_str(decoded_inst.opr.src0, 0b1), disasm::operand_to_str(decoded_inst.opr.src1, 0b1), disasm::operand_to_str(decoded_inst.opr.src2, 0b1), sn);
return true;
}
} // namespace shader::usse
+9 -14
View File
@@ -1,5 +1,5 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,32 +15,27 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/translator.h>
#include <util/log.h>
bool shader::usse::USSETranslatorVisitor::illegal22() {
namespace shader::usse {
bool USSETranslatorVisitor::illegal22() {
LOG_ERROR("Illegal shader opcode: 22");
return false;
}
bool shader::usse::USSETranslatorVisitor::illegal23() {
bool USSETranslatorVisitor::illegal23() {
LOG_ERROR("Illegal shader opcode: 23");
return false;
}
bool shader::usse::USSETranslatorVisitor::illegal24() {
bool USSETranslatorVisitor::illegal24() {
LOG_ERROR("Illegal shader opcode: 24");
return false;
}
bool shader::usse::USSETranslatorVisitor::illegal27() {
bool USSETranslatorVisitor::illegal27() {
LOG_ERROR("Illegal shader opcode: 27");
return false;
}
}
} // namespace shader::usse
+5 -22
View File
@@ -1,5 +1,5 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,19 +15,11 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_translator.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_types.h>
#include <shader/disasm.h>
#include <shader/translator.h>
#include <util/log.h>
using namespace shader;
using namespace usse;
namespace shader::usse {
bool USSETranslatorVisitor::phas(
Imm1 sprvv,
Imm1 end,
@@ -105,13 +97,4 @@ bool USSETranslatorVisitor::smlsi(
return true;
}
bool USSETranslatorVisitor::kill(
ShortPredicate pred) {
LOG_DISASM("{:016x}: KILL {}", m_instr, disasm::s_predicate_str(pred));
m_b.setLine(m_recompiler.cur_pc);
m_b.makeDiscard();
return true;
}
} // namespace shader::usse
+21 -233
View File
@@ -1,6 +1,5 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
// Copyright (C) 2022 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -16,160 +15,13 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/usse_decoder_helpers.h>
#include <shader/usse_disasm.h>
#include <shader/usse_translator.h>
#include <shader/usse_types.h>
#include <shader/decoder_helpers.h>
#include <shader/disasm.h>
#include <shader/translator.h>
#include <shader/types.h>
#include <util/log.h>
#include <SPIRV/GLSL.std.450.h>
#include <SPIRV/SpvBuilder.h>
using namespace shader;
using namespace usse;
spv::Id shader::usse::USSETranslatorVisitor::do_fetch_texture(const spv::Id tex, const Coord &coord, const DataType dest_type, const int lod_mode, const spv::Id lod) {
auto coord_id = coord.first;
if (coord.second != static_cast<int>(DataType::F32)) {
coord_id = m_b.createOp(spv::OpVectorExtractDynamic, type_f32, { m_b.createLoad(coord_id, spv::NoPrecision), m_b.makeIntConstant(0) });
coord_id = utils::unpack_one(m_b, m_util_funcs, m_features, coord_id, static_cast<DataType>(coord.second));
// Shuffle if number of components is larger than 2
if (m_b.getNumComponents(coord_id) > 2) {
coord_id = m_b.createOp(spv::OpVectorShuffle, m_b.makeVectorType(type_f32, 2), { coord_id, coord_id, 0, 1 });
}
}
if (m_b.isPointer(coord_id)) {
coord_id = m_b.createLoad(coord_id, spv::NoPrecision);
}
assert(m_b.getTypeClass(m_b.getContainedTypeId(m_b.getTypeId(coord_id))) == spv::OpTypeFloat);
spv::Id image_sample = spv::NoResult;
if (lod == spv::NoResult) {
if (lod_mode == 4) {
image_sample = m_b.createOp(spv::OpImageSampleProjImplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id });
} else {
image_sample = m_b.createOp(spv::OpImageSampleImplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id });
}
} else {
if (lod_mode == 2) {
image_sample = m_b.createOp(spv::OpImageSampleExplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id, spv::ImageOperandsLodMask, lod });
} else if (lod_mode == 3) {
spv::Id ddx = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { lod, lod, 0, 1 });
spv::Id ddy = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { lod, lod, 2, 3 });
image_sample = m_b.createOp(spv::OpImageSampleExplicitLod, type_f32_v[4], { m_b.createLoad(tex, spv::NoPrecision), coord_id, spv::ImageOperandsGradMask, ddx, ddy });
}
}
if (dest_type == DataType::F16) {
// Pack them
spv::Id pack1 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { image_sample, image_sample, 0, 1 });
pack1 = utils::pack_one(m_b, m_util_funcs, m_features, pack1, DataType::F16);
spv::Id pack2 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { image_sample, image_sample, 2, 3 });
pack2 = utils::pack_one(m_b, m_util_funcs, m_features, pack2, DataType::F16);
image_sample = m_b.createCompositeConstruct(type_f32_v[2], { pack1, pack2 });
}
if (dest_type == DataType::UINT8) {
image_sample = utils::convert_to_int(m_b, image_sample, DataType::UINT8, true);
image_sample = utils::pack_one(m_b, m_util_funcs, m_features, image_sample, DataType::UINT8);
}
return image_sample;
}
void shader::usse::USSETranslatorVisitor::do_texture_queries(const NonDependentTextureQueryCallInfos &texture_queries, const spv::Id translation_state_id) {
Operand store_op;
store_op.bank = RegisterBank::PRIMATTR;
store_op.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
store_op.type = DataType::F32;
for (auto &texture_query : texture_queries) {
Imm4 dest_mask;
switch (texture_query.store_type) {
case (int)DataType::F16: {
dest_mask = 0b11;
break;
}
case (int)DataType::F32: {
dest_mask = 0b1111;
break;
}
case (int)DataType::UINT8: {
dest_mask = 0b1;
break;
}
default:
assert(false);
}
bool proj = (texture_query.prod_pos >= 0);
shader::usse::Coord coord_inst = texture_query.coord;
if (texture_query.prod_pos >= 0) {
coord_inst.first = m_b.createOp(spv::OpVectorShuffle, type_f32_v[3], { texture_query.coord.first, texture_query.coord.first, 0, 1, static_cast<spv::Id>(texture_query.prod_pos) });
proj = true;
}
spv::Id fetch_result = do_fetch_texture(texture_query.sampler, coord_inst, static_cast<DataType>(texture_query.store_type), proj ? 4 : 0, 0);
store_op.num = texture_query.dest_offset;
if (static_cast<DataType>(texture_query.store_type) == DataType::UNK) {
// Manual check
spv::Id sampler_integral_query_format = m_b.createAccessChain(spv::StorageClassPrivate, translation_state_id, { m_b.makeIntConstant(4), m_b.makeIntConstant(texture_query.sampler_index / 4), m_b.makeIntConstant(texture_query.sampler_index % 4) });
spv::Id bool_type = m_b.makeBoolType();
spv::Builder::If if_builder(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeFloatConstant(INTEGRAL_TEX_QUERY_TYPE_8BIT_SIGNED)), spv::SelectionControlMaskNone, m_b);
spv::Id packed8 = utils::convert_to_int(m_b, fetch_result, DataType::INT8, true);
packed8 = utils::pack_one(m_b, m_util_funcs, m_features, packed8, DataType::INT8);
dest_mask = 0b1;
store(store_op, packed8, dest_mask);
if_builder.makeBeginElse();
spv::Builder::If if_builder_2(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeIntConstant(INTEGRAL_TEX_QUERY_TYPE_8BIT_UNSIGNED)), spv::SelectionControlMaskNone, m_b);
packed8 = utils::convert_to_int(m_b, fetch_result, DataType::UINT8, true);
packed8 = utils::pack_one(m_b, m_util_funcs, m_features, packed8, DataType::UINT8);
dest_mask = 0b1;
store(store_op, packed8, dest_mask);
if_builder_2.makeBeginElse();
spv::Builder::If if_builder_3(m_b.createBinOp(spv::OpFOrdGreaterThanEqual, bool_type, sampler_integral_query_format, m_b.makeIntConstant(INTEGRAL_TEX_QUERY_TYPE_16BIT)), spv::SelectionControlMaskNone, m_b);
spv::Id pack1 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { fetch_result, fetch_result, 0, 1 });
pack1 = utils::pack_one(m_b, m_util_funcs, m_features, pack1, DataType::F16);
spv::Id pack2 = m_b.createOp(spv::OpVectorShuffle, type_f32_v[2], { fetch_result, fetch_result, 2, 3 });
pack2 = utils::pack_one(m_b, m_util_funcs, m_features, pack2, DataType::F16);
spv::Id packedu16 = m_b.createCompositeConstruct(type_f32_v[2], { pack1, pack2 });
dest_mask = 0b11;
store(store_op, packedu16, dest_mask);
if_builder_3.makeBeginElse();
dest_mask = 0b1111;
store(store_op, fetch_result, dest_mask);
if_builder_3.makeEndIf();
if_builder_2.makeEndIf();
if_builder.makeEndIf();
} else {
store(store_op, fetch_result, dest_mask);
}
}
}
namespace shader::usse {
bool USSETranslatorVisitor::smp(
ExtPredicate pred,
Imm1 skipinv,
@@ -194,12 +46,6 @@ bool USSETranslatorVisitor::smp(
Imm7 src0_n,
Imm7 src1_n,
Imm7 src2_n) {
// LOD mode: none, bias, replace, gradient
if ((lod_mode != 0) && (lod_mode != 2) && (lod_mode != 3)) {
LOG_ERROR("Sampler LOD replace not implemented!");
return true;
}
constexpr DataType tb_dest_fmt[] = {
DataType::F32,
DataType::UNK,
@@ -208,96 +54,38 @@ bool USSETranslatorVisitor::smp(
};
// Decode dest
Instruction inst;
inst.opr.dest.bank = (dest_use_pa) ? RegisterBank::PRIMATTR : RegisterBank::TEMP;
inst.opr.dest.num = dest_n;
inst.opr.dest.type = tb_dest_fmt[fconv_type];
decoded_inst.opr.dest.bank = (dest_use_pa) ? RegisterBank::PRIMATTR : RegisterBank::TEMP;
decoded_inst.opr.dest.num = dest_n;
decoded_inst.opr.dest.type = tb_dest_fmt[fconv_type];
// Decode src0
inst.opr.src0 = decode_src0(inst.opr.src0, src0_n, src0_bank, src0_ext, true, 8, m_second_program);
inst.opr.src0.type = (src0_type == 0) ? DataType::F32 : ((src0_type == 1) ? DataType::F16 : DataType::C10);
decoded_inst.opr.src0 = decode_src0(decoded_inst.opr.src0, src0_n, src0_bank, src0_ext, true, 8, m_second_program);
decoded_inst.opr.src0.type = (src0_type == 0) ? DataType::F32 : ((src0_type == 1) ? DataType::F16 : DataType::C10);
inst.opr.src1 = decode_src12(inst.opr.src1, src1_n, src1_bank, src1_ext, true, 8, m_second_program);
decoded_inst.opr.src1 = decode_src12(decoded_inst.opr.src1, src1_n, src1_bank, src1_ext, true, 8, m_second_program);
inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
inst.opr.dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.src1.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.src0.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
decoded_inst.opr.dest.swizzle = SWIZZLE_CHANNEL_4_DEFAULT;
// Base 0, turn it to base 1
dim += 1;
spv::Id coord_mask = 0b0011;
std::uint32_t coord_mask = 0b0011;
if (dim == 3) {
coord_mask = 0b0111;
} else if (dim == 1) {
coord_mask = 0b0001;
}
LOG_DISASM("{:016x}: {}SMP{}d.{}.{} {} {} {} {}", m_instr, disasm::e_predicate_str(pred), dim, disasm::data_type_str(inst.opr.dest.type), disasm::data_type_str(inst.opr.src0.type),
disasm::operand_to_str(inst.opr.dest, 0b0001), disasm::operand_to_str(inst.opr.src0, coord_mask), disasm::operand_to_str(inst.opr.src1, 0b0000), (lod_mode == 0) ? "" : disasm::operand_to_str(inst.opr.src2, 0b0001));
m_b.setLine(m_recompiler.cur_pc);
// Generate simple stuff
// Load the coord
spv::Id coord = load(inst.opr.src0, coord_mask);
if (coord == spv::NoResult) {
LOG_ERROR("Coord not loaded");
return false;
}
if (dim == 1) {
// It should be a line, so Y should be zero. There are only two dimensions texture, so this is a guess (seems concise)
coord = m_b.createCompositeConstruct(m_b.makeVectorType(m_b.makeFloatType(32), 2), { coord, m_b.makeIntConstant(0) });
}
spv::Id sampler = spv::NoResult;
if (m_spirv_params.samplers.count(inst.opr.src1.num)) {
sampler = m_spirv_params.samplers.at(inst.opr.src1.num);
} else {
LOG_ERROR("Can't get the sampler (sampler doesn't exist!)");
return true;
}
// Either LOD number or gradient number
spv::Id extra = spv::NoResult;
if (lod_mode != 0) {
inst.opr.src2 = decode_src12(inst.opr.src2, src2_n, src2_bank, src2_ext, true, 8, m_second_program);
inst.opr.src2.type = inst.opr.src0.type;
switch (lod_mode) {
case 2:
extra = load(inst.opr.src2, 0b1);
break;
case 3:
extra = load(inst.opr.src2, 0b1111);
break;
default:
break;
}
decoded_inst.opr.src2 = decode_src12(decoded_inst.opr.src2, src2_n, src2_bank, src2_ext, true, 8, m_second_program);
decoded_inst.opr.src2.type = decoded_inst.opr.src0.type;
}
spv::Id result = do_fetch_texture(sampler, { coord, static_cast<int>(DataType::F32) }, DataType::F32, lod_mode, extra);
switch (sb_mode) {
case 0:
case 1:
store(inst.opr.dest, result, 0b1111);
break;
case 3: {
// TODO: figure out what to fill here
// store(inst.opr.dest, stub, 0b1111);
store(inst.opr.dest, result, 0b1111);
break;
}
default: {
LOG_ERROR("Unsupported sb_mode: {}", sb_mode);
}
}
LOG_DISASM("{:016x}: {}SMP{}d.{}.{} {} {} {} {}", m_instr, disasm::e_predicate_str(pred), dim, disasm::data_type_str(decoded_inst.opr.dest.type), disasm::data_type_str(decoded_inst.opr.src0.type),
disasm::operand_to_str(decoded_inst.opr.dest, 0b0001), disasm::operand_to_str(decoded_inst.opr.src0, coord_mask), disasm::operand_to_str(decoded_inst.opr.src1, 0b0000), (lod_mode == 0) ? "" : disasm::operand_to_str(decoded_inst.opr.src2, 0b0001));
return true;
}
} // namespace shader::usse
+24
View File
@@ -0,0 +1,24 @@
// Vita3K emulator project
// Copyright (C) 2021 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <shader/types.h>
namespace shader::usse {
bool is_sub_opcode(Opcode test_op) {
return (test_op == Opcode::VSUB) || (test_op == Opcode::VF16SUB) || (test_op == Opcode::ISUB8) || (test_op == Opcode::ISUB16) || (test_op == Opcode::ISUB32) || (test_op == Opcode::ISUBU8) || (test_op == Opcode::ISUBU16) || (test_op == Opcode::ISUBU32) || (test_op == Opcode::FPSUB8);
}
} // namespace shader::usse
@@ -16,7 +16,7 @@
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <gtest/gtest.h>
#include <shader/usse_program_analyzer.h>
#include <shader/program_analyzer.h>
#include <unordered_map>