diff options
20 files changed, 281 insertions, 217 deletions
diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh index b80868635..e46ee0abb 100644 --- a/.ci/scripts/common/post-upload.sh +++ b/.ci/scripts/common/post-upload.sh @@ -4,9 +4,13 @@ cp license.txt "$DIR_NAME" cp README.md "$DIR_NAME" +tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt +cp "${REV_NAME}-source.tar.xz" "$DIR_NAME" + tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME" mv "$DIR_NAME" $RELEASE_NAME +mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME 7z a "$REV_NAME.7z" $RELEASE_NAME diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh index ecd77e267..fe4e6b2ac 100644 --- a/.ci/scripts/linux/upload.sh +++ b/.ci/scripts/linux/upload.sh @@ -7,9 +7,9 @@ ARCHIVE_NAME="${REV_NAME}.tar.xz" COMPRESSION_FLAGS="-cJvf" if [ "${RELEASE_NAME}" = "mainline" ]; then - DIR_NAME="${REV_NAME}_${RELEASE_NAME}" -else DIR_NAME="${REV_NAME}" +else + DIR_NAME="${REV_NAME}_${RELEASE_NAME}" fi mkdir "$DIR_NAME" diff --git a/.ci/scripts/windows/upload.ps1 b/.ci/scripts/windows/upload.ps1 index 9fb99eaa3..b9b8b4af8 100644 --- a/.ci/scripts/windows/upload.ps1 +++ b/.ci/scripts/windows/upload.ps1 @@ -1,19 +1,23 @@ param($BUILD_NAME) -$GITDATE = $(git show -s --date=short --format='%ad') -replace "-","" +$GITDATE = $(git show -s --date=short --format='%ad') -replace "-", "" $GITREV = $(git show -s --format='%h') if ("$BUILD_NAME" -eq "mainline") { - $RELEASE_DIST = "yuzu-windows-msvc-$BUILD_NAME" -} else { $RELEASE_DIST = "yuzu-windows-msvc" } +else { + $RELEASE_DIST = "yuzu-windows-msvc-$BUILD_NAME" +} $MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", "" $MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", "" $MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", "" $MSVC_TAR = "yuzu-windows-msvc-$GITDATE-$GITREV.tar" -replace " ", "" $MSVC_TARXZ = "yuzu-windows-msvc-$GITDATE-$GITREV.tar.xz" -replace " ", "" +$MSVC_SOURCE = "yuzu-windows-msvc-source-$GITDATE-$GITREV" -replace " ", "" +$MSVC_SOURCE_TAR = "$MSVC_SOURCE.tar" +$MSVC_SOURCE_TARXZ = "$MSVC_SOURCE_TAR.xz" $env:BUILD_ZIP = $MSVC_BUILD_ZIP $env:BUILD_SYMBOLS = $MSVC_BUILD_PDB @@ -21,19 +25,33 @@ $env:BUILD_UPDATE = $MSVC_SEVENZIP $BUILD_DIR = ".\build\bin\Release" +# Upload debugging symbols mkdir pdb Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb 7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb rm "$BUILD_DIR\*.pdb" + +# Create artifact directories mkdir $RELEASE_DIST +mkdir $MSVC_SOURCE mkdir "artifacts" +# Build a tar.xz for the source of the release +Copy-Item .\license.txt -Destination $MSVC_SOURCE +Copy-Item .\README.md -Destination $MSVC_SOURCE +Copy-Item .\src -Recurse -Destination $MSVC_SOURCE +Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE +Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE +Copy-Item .\CMakeModules -Recurse -Destination $MSVC_SOURCE +7z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE +7z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR + +# Build the final release artifacts +Copy-Item $MSVC_SOURCE_TARXZ -Destination $RELEASE_DIST Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse rm "$RELEASE_DIST\*.exe" Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST -Copy-Item .\license.txt -Destination $RELEASE_DIST -Copy-Item .\README.md -Destination $RELEASE_DIST 7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\* 7z a $MSVC_SEVENZIP $RELEASE_DIST diff --git a/.ci/scripts/windows/upload.sh b/.ci/scripts/windows/upload.sh index 41d87159f..ebf5b7dc1 100644 --- a/.ci/scripts/windows/upload.sh +++ b/.ci/scripts/windows/upload.sh @@ -7,9 +7,9 @@ ARCHIVE_NAME="${REV_NAME}.tar.gz" COMPRESSION_FLAGS="-czvf" if [ "${RELEASE_NAME}" = "mainline" ]; then - DIR_NAME="${REV_NAME}_${RELEASE_NAME}" -else DIR_NAME="${REV_NAME}" +else + DIR_NAME="${REV_NAME}_${RELEASE_NAME}" fi mkdir "$DIR_NAME" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ccb8fc5d7..5d4b6f9da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -**The Contributor's Guide has moved to [the Yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).** +**The Contributor's Guide has moved to [the yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).** diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 63b3a8205..4408b5001 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -12,6 +12,10 @@ #include <utility> #include <vector> +#include <boost/icl/interval_map.hpp> +#include <boost/icl/interval_set.hpp> +#include <boost/range/iterator_range.hpp> + #include "common/alignment.h" #include "common/common_types.h" #include "core/core.h" diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index f1b89165d..35e5214a5 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -394,7 +394,8 @@ Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params, params.disk_cache.SaveRaw(ShaderDiskCacheRaw( params.unique_identifier, GetProgramType(program_type), program_code, program_code_b)); - ConstBufferLocker locker(GetEnginesShaderType(GetProgramType(program_type))); + ConstBufferLocker locker(GetEnginesShaderType(GetProgramType(program_type)), + params.system.GPU().Maxwell3D()); const ShaderIR ir(program_code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker); // TODO(Rodrigo): Handle VertexA shaders // std::optional<ShaderIR> ir_b; @@ -410,7 +411,8 @@ Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, Prog params.disk_cache.SaveRaw( ShaderDiskCacheRaw(params.unique_identifier, ProgramType::Compute, code)); - ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute); + ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute, + params.system.GPU().KeplerCompute()); const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker); return std::shared_ptr<CachedShader>(new CachedShader( params, ProgramType::Compute, GLShader::GetEntries(ir), std::move(code), {})); diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 92ee8459e..0ce59a852 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1670,7 +1670,7 @@ private: const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; return {GenerateTexture(operation, "Gather", - {TextureArgument{type, meta->component}, TextureAoffi{}}) + + {TextureAoffi{}, TextureArgument{type, meta->component}}) + GetSwizzle(meta->element), Type::Float}; } diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index d47c63d9f..b427ac873 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -16,7 +16,9 @@ #include "video_core/shader/shader_ir.h" namespace VideoCommon::Shader { + namespace { + using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; @@ -68,15 +70,15 @@ struct CFGRebuildState { const ProgramCode& program_code; ConstBufferLocker& locker; u32 start{}; - std::vector<BlockInfo> block_info{}; - std::list<u32> inspect_queries{}; - std::list<Query> queries{}; - std::unordered_map<u32, u32> registered{}; - std::set<u32> labels{}; - std::map<u32, u32> ssy_labels{}; - std::map<u32, u32> pbk_labels{}; - std::unordered_map<u32, BlockStack> stacks{}; - ASTManager* manager; + std::vector<BlockInfo> block_info; + std::list<u32> inspect_queries; + std::list<Query> queries; + std::unordered_map<u32, u32> registered; + std::set<u32> labels; + std::map<u32, u32> ssy_labels; + std::map<u32, u32> pbk_labels; + std::unordered_map<u32, BlockStack> stacks; + ASTManager* manager{}; }; enum class BlockCollision : u32 { None, Found, Inside }; @@ -109,7 +111,7 @@ BlockInfo& CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) { } Pred GetPredicate(u32 index, bool negated) { - return static_cast<Pred>(index + (negated ? 8 : 0)); + return static_cast<Pred>(static_cast<u64>(index) + (negated ? 8ULL : 0ULL)); } /** @@ -136,15 +138,13 @@ struct BranchIndirectInfo { s32 relative_position{}; }; -std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, - u32 start_address, u32 current_position) { - const u32 shader_start = state.start; - u32 pos = current_position; - BranchIndirectInfo result{}; - u64 track_register = 0; +struct BufferInfo { + u32 index; + u32 offset; +}; - // Step 0 Get BRX Info - const Instruction instr = {state.program_code[pos]}; +std::optional<std::pair<s32, u64>> GetBRXInfo(const CFGRebuildState& state, u32& pos) { + const Instruction instr = state.program_code[pos]; const auto opcode = OpCode::Decode(instr); if (opcode->get().GetId() != OpCode::Id::BRX) { return std::nullopt; @@ -152,86 +152,94 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& if (instr.brx.constant_buffer != 0) { return std::nullopt; } - track_register = instr.gpr8.Value(); - result.relative_position = instr.brx.GetBranchExtend(); - pos--; - bool found_track = false; + --pos; + return std::make_pair(instr.brx.GetBranchExtend(), instr.gpr8.Value()); +} - // Step 1 Track LDC - while (pos >= shader_start) { - if (IsSchedInstruction(pos, shader_start)) { - pos--; +template <typename Result, typename TestCallable, typename PackCallable> +// requires std::predicate<TestCallable, Instruction, const OpCode::Matcher&> +// requires std::invocable<PackCallable, Instruction, const OpCode::Matcher&> +std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test, + PackCallable pack) { + for (; pos >= state.start; --pos) { + if (IsSchedInstruction(pos, state.start)) { continue; } - const Instruction instr = {state.program_code[pos]}; + const Instruction instr = state.program_code[pos]; const auto opcode = OpCode::Decode(instr); - if (opcode->get().GetId() == OpCode::Id::LD_C) { - if (instr.gpr0.Value() == track_register && - instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) { - result.buffer = instr.cbuf36.index.Value(); - result.offset = static_cast<u32>(instr.cbuf36.GetOffset()); - track_register = instr.gpr8.Value(); - pos--; - found_track = true; - break; - } + if (!opcode) { + continue; + } + if (test(instr, opcode->get())) { + --pos; + return std::make_optional(pack(instr, opcode->get())); } - pos--; } + return std::nullopt; +} - if (!found_track) { - return std::nullopt; - } - found_track = false; +std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state, u32& pos, + u64 brx_tracked_register) { + return TrackInstruction<std::pair<BufferInfo, u64>>( + state, pos, + [brx_tracked_register](auto instr, const auto& opcode) { + return opcode.GetId() == OpCode::Id::LD_C && + instr.gpr0.Value() == brx_tracked_register && + instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single; + }, + [](auto instr, const auto& opcode) { + const BufferInfo info = {static_cast<u32>(instr.cbuf36.index.Value()), + static_cast<u32>(instr.cbuf36.GetOffset())}; + return std::make_pair(info, instr.gpr8.Value()); + }); +} - // Step 2 Track SHL - while (pos >= shader_start) { - if (IsSchedInstruction(pos, shader_start)) { - pos--; - continue; - } - const Instruction instr = state.program_code[pos]; - const auto opcode = OpCode::Decode(instr); - if (opcode->get().GetId() == OpCode::Id::SHL_IMM) { - if (instr.gpr0.Value() == track_register) { - track_register = instr.gpr8.Value(); - pos--; - found_track = true; - break; - } - } - pos--; +std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, + u64 ldc_tracked_register) { + return TrackInstruction<u64>(state, pos, + [ldc_tracked_register](auto instr, const auto& opcode) { + return opcode.GetId() == OpCode::Id::SHL_IMM && + instr.gpr0.Value() == ldc_tracked_register; + }, + [](auto instr, const auto&) { return instr.gpr8.Value(); }); +} + +std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, + u64 shl_tracked_register) { + return TrackInstruction<u32>(state, pos, + [shl_tracked_register](auto instr, const auto& opcode) { + return opcode.GetId() == OpCode::Id::IMNMX_IMM && + instr.gpr0.Value() == shl_tracked_register; + }, + [](auto instr, const auto&) { + return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); + }); +} + +std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { + const auto brx_info = GetBRXInfo(state, pos); + if (!brx_info) { + return std::nullopt; } + const auto [relative_position, brx_tracked_register] = *brx_info; - if (!found_track) { + const auto ldc_info = TrackLDC(state, pos, brx_tracked_register); + if (!ldc_info) { return std::nullopt; } - found_track = false; + const auto [buffer_info, ldc_tracked_register] = *ldc_info; - // Step 3 Track IMNMX - while (pos >= shader_start) { - if (IsSchedInstruction(pos, shader_start)) { - pos--; - continue; - } - const Instruction instr = state.program_code[pos]; - const auto opcode = OpCode::Decode(instr); - if (opcode->get().GetId() == OpCode::Id::IMNMX_IMM) { - if (instr.gpr0.Value() == track_register) { - track_register = instr.gpr8.Value(); - result.entries = instr.alu.GetSignedImm20_20() + 1; - pos--; - found_track = true; - break; - } - } - pos--; + const auto shl_tracked_register = TrackSHLRegister(state, pos, ldc_tracked_register); + if (!shl_tracked_register) { + return std::nullopt; } - if (!found_track) { + const auto entries = TrackIMNMXValue(state, pos, *shl_tracked_register); + if (!entries) { return std::nullopt; } - return result; + + return BranchIndirectInfo{buffer_info.index, buffer_info.offset, *entries, relative_position}; } std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) { @@ -420,30 +428,30 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) break; } case OpCode::Id::BRX: { - auto tmp = TrackBranchIndirectInfo(state, address, offset); - if (tmp) { - auto result = *tmp; - std::vector<CaseBranch> branches{}; - s32 pc_target = offset + result.relative_position; - for (u32 i = 0; i < result.entries; i++) { - auto k = state.locker.ObtainKey(result.buffer, result.offset + i * 4); - if (!k) { - return {ParseResult::AbnormalFlow, parse_info}; - } - u32 value = *k; - u32 target = static_cast<u32>((value >> 3) + pc_target); - insert_label(state, target); - branches.emplace_back(value, target); - } - parse_info.end_address = offset; - parse_info.branch_info = MakeBranchInfo<MultiBranch>( - static_cast<u32>(instr.gpr8.Value()), std::move(branches)); - - return {ParseResult::ControlCaught, parse_info}; - } else { + const auto tmp = TrackBranchIndirectInfo(state, offset); + if (!tmp) { LOG_WARNING(HW_GPU, "BRX Track Unsuccesful"); + return {ParseResult::AbnormalFlow, parse_info}; } - return {ParseResult::AbnormalFlow, parse_info}; + + const auto result = *tmp; + const s32 pc_target = offset + result.relative_position; + std::vector<CaseBranch> branches; + for (u32 i = 0; i < result.entries; i++) { + auto key = state.locker.ObtainKey(result.buffer, result.offset + i * 4); + if (!key) { + return {ParseResult::AbnormalFlow, parse_info}; + } + u32 value = *key; + u32 target = static_cast<u32>((value >> 3) + pc_target); + insert_label(state, target); + branches.emplace_back(value, target); + } + parse_info.end_address = offset; + parse_info.branch_info = MakeBranchInfo<MultiBranch>( + static_cast<u32>(instr.gpr8.Value()), std::move(branches)); + + return {ParseResult::ControlCaught, parse_info}; } default: break; diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp index 1473c282a..fcedd2af6 100644 --- a/src/video_core/shader/decode/arithmetic.cpp +++ b/src/video_core/shader/decode/arithmetic.cpp @@ -43,12 +43,12 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { case OpCode::Id::FMUL_IMM: { // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. if (instr.fmul.tab5cb8_2 != 0) { - LOG_WARNING(HW_GPU, "FMUL tab5cb8_2({}) is not implemented", - instr.fmul.tab5cb8_2.Value()); + LOG_DEBUG(HW_GPU, "FMUL tab5cb8_2({}) is not implemented", + instr.fmul.tab5cb8_2.Value()); } if (instr.fmul.tab5c68_0 != 1) { - LOG_WARNING(HW_GPU, "FMUL tab5cb8_0({}) is not implemented", - instr.fmul.tab5c68_0.Value()); + LOG_DEBUG(HW_GPU, "FMUL tab5cb8_0({}) is not implemented", + instr.fmul.tab5c68_0.Value()); } op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b); @@ -144,10 +144,11 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { case OpCode::Id::RRO_C: case OpCode::Id::RRO_R: case OpCode::Id::RRO_IMM: { + LOG_DEBUG(HW_GPU, "(STUBBED) RRO used"); + // Currently RRO is only implemented as a register move. op_b = GetOperandAbsNegFloat(op_b, instr.alu.abs_b, instr.alu.negate_b); SetRegister(bb, instr.gpr0, op_b); - LOG_WARNING(HW_GPU, "RRO instruction is incomplete"); break; } default: diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp index b06cbe441..ee7d9a29d 100644 --- a/src/video_core/shader/decode/arithmetic_half.cpp +++ b/src/video_core/shader/decode/arithmetic_half.cpp @@ -21,8 +21,8 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { if (opcode->get().GetId() == OpCode::Id::HADD2_C || opcode->get().GetId() == OpCode::Id::HADD2_R) { - if (instr.alu_half.ftz != 0) { - LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); + if (instr.alu_half.ftz == 0) { + LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); } } diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp index 6466fc011..d179b9873 100644 --- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp +++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp @@ -19,12 +19,12 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) { const auto opcode = OpCode::Decode(instr); if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { - if (instr.alu_half_imm.ftz != 0) { - LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); + if (instr.alu_half_imm.ftz == 0) { + LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); } } else { - if (instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None) { - LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); + if (instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::FTZ) { + LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); } } diff --git a/src/video_core/shader/decode/ffma.cpp b/src/video_core/shader/decode/ffma.cpp index ca2f39e8d..5973588d6 100644 --- a/src/video_core/shader/decode/ffma.cpp +++ b/src/video_core/shader/decode/ffma.cpp @@ -19,10 +19,10 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented"); if (instr.ffma.tab5980_0 != 1) { - LOG_WARNING(HW_GPU, "FFMA tab5980_0({}) not implemented", instr.ffma.tab5980_0.Value()); + LOG_DEBUG(HW_GPU, "FFMA tab5980_0({}) not implemented", instr.ffma.tab5980_0.Value()); } if (instr.ffma.tab5980_1 != 0) { - LOG_WARNING(HW_GPU, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value()); + LOG_DEBUG(HW_GPU, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value()); } const Node op_a = GetRegister(instr.gpr8); diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 48ca7a4af..848e46874 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp @@ -20,8 +20,8 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); - if (instr.hset2.ftz != 0) { - LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); + if (instr.hset2.ftz == 0) { + LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); } Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp index fec8f2dbe..310655619 100644 --- a/src/video_core/shader/decode/half_set_predicate.cpp +++ b/src/video_core/shader/decode/half_set_predicate.cpp @@ -19,7 +19,9 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); - LOG_DEBUG(HW_GPU, "ftz={}", static_cast<u32>(instr.hsetp2.ftz)); + if (instr.hsetp2.ftz != 0) { + LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); + } Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a); op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index ca690b58b..bb926a132 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -44,10 +44,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { bool is_bindless = false; switch (opcode->get().GetId()) { case OpCode::Id::TEX: { - if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete"); - } - const TextureType texture_type{instr.tex.texture_type}; const bool is_array = instr.tex.array != 0; const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI); @@ -62,10 +58,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI), "AOFFI is not implemented"); - if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete"); - } - const TextureType texture_type{instr.tex_b.texture_type}; const bool is_array = instr.tex_b.array != 0; const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI); @@ -82,10 +74,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { const bool depth_compare = instr.texs.UsesMiscMode(TextureMiscMode::DC); const auto process_mode = instr.texs.GetTextureProcessMode(); - if (instr.texs.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TEXS.NODEP implementation is incomplete"); - } - const Node4 components = GetTexsCode(instr, texture_type, process_mode, depth_compare, is_array); @@ -107,10 +95,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP), "PTP is not implemented"); - if (instr.tld4.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TLD4.NODEP implementation is incomplete"); - } - const auto texture_type = instr.tld4.texture_type.Value(); const bool depth_compare = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::DC) : instr.tld4.UsesMiscMode(TextureMiscMode::DC); @@ -125,9 +109,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { case OpCode::Id::TLD4S: { UNIMPLEMENTED_IF_MSG(instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI), "AOFFI is not implemented"); - if (instr.tld4s.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TLD4S.NODEP implementation is incomplete"); - } const bool depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC); const Node op_a = GetRegister(instr.gpr8); @@ -164,10 +145,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { is_bindless = true; [[fallthrough]]; case OpCode::Id::TXQ: { - if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TXQ.NODEP implementation is incomplete"); - } - // TODO: The new commits on the texture refactor, change the way samplers work. // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. @@ -205,10 +182,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), "NDV is not implemented"); - if (instr.tmml.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TMML.NODEP implementation is incomplete"); - } - auto texture_type = instr.tmml.texture_type.Value(); const bool is_array = instr.tmml.array != 0; const auto& sampler = @@ -254,10 +227,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.tld.ms, "MS is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tld.cl, "CL is not implemented"); - if (instr.tld.nodep_flag) { - LOG_WARNING(HW_GPU, "TLD.NODEP implementation is incomplete"); - } - WriteTexInstructionFloat(bb, instr, GetTldCode(instr)); break; } @@ -269,10 +238,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { "AOFFI is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented"); - if (instr.tlds.UsesMiscMode(TextureMiscMode::NODEP)) { - LOG_WARNING(HW_GPU, "TLDS.NODEP implementation is incomplete"); - } - const Node4 components = GetTldsCode(instr, texture_type, is_array); if (instr.tlds.fp32_flag) { diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index dc149d2ed..6683f459f 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <cstdlib> #include <mutex> #include <string> #include <LUrlParser.h> +#include <fmt/format.h> #include <httplib.h> #include "common/common_types.h" #include "common/logging/log.h" @@ -16,10 +18,10 @@ namespace WebService { constexpr std::array<const char, 1> API_VERSION{'1'}; -constexpr u32 HTTP_PORT = 80; -constexpr u32 HTTPS_PORT = 443; +constexpr int HTTP_PORT = 80; +constexpr int HTTPS_PORT = 443; -constexpr u32 TIMEOUT_SECONDS = 30; +constexpr std::size_t TIMEOUT_SECONDS = 30; struct Client::Impl { Impl(std::string host, std::string username, std::string token) @@ -31,8 +33,9 @@ struct Client::Impl { } /// A generic function handles POST, GET and DELETE request together - Common::WebResult GenericJson(const std::string& method, const std::string& path, - const std::string& data, bool allow_anonymous) { + Common::WebResult GenericRequest(const std::string& method, const std::string& path, + const std::string& data, bool allow_anonymous, + const std::string& accept) { if (jwt.empty()) { UpdateJWT(); } @@ -43,11 +46,11 @@ struct Client::Impl { "Credentials needed"}; } - auto result = GenericJson(method, path, data, jwt); + auto result = GenericRequest(method, path, data, accept, jwt); if (result.result_string == "401") { // Try again with new JWT UpdateJWT(); - result = GenericJson(method, path, data, jwt); + result = GenericRequest(method, path, data, accept, jwt); } return result; @@ -56,12 +59,13 @@ struct Client::Impl { /** * A generic function with explicit authentication method specified * JWT is used if the jwt parameter is not empty - * username + token is used if jwt is empty but username and token are not empty - * anonymous if all of jwt, username and token are empty + * username + token is used if jwt is empty but username and token are + * not empty anonymous if all of jwt, username and token are empty */ - Common::WebResult GenericJson(const std::string& method, const std::string& path, - const std::string& data, const std::string& jwt = "", - const std::string& username = "", const std::string& token = "") { + Common::WebResult GenericRequest(const std::string& method, const std::string& path, + const std::string& data, const std::string& accept, + const std::string& jwt = "", const std::string& username = "", + const std::string& token = "") { if (cli == nullptr) { auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); int port; @@ -132,8 +136,7 @@ struct Client::Impl { return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; } - if (content_type->second.find("application/json") == std::string::npos && - content_type->second.find("text/html; charset=utf-8") == std::string::npos) { + if (content_type->second.find(accept) == std::string::npos) { LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, content_type->second); return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; @@ -147,7 +150,7 @@ struct Client::Impl { return; } - auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); + auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token); if (result.result_code != Common::WebResult::Code::Success) { LOG_ERROR(WebService, "UpdateJWT failed"); } else { @@ -180,16 +183,29 @@ Client::~Client() = default; Common::WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) { - return impl->GenericJson("POST", path, data, allow_anonymous); + return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json"); } Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { - return impl->GenericJson("GET", path, "", allow_anonymous); + return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json"); } Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous) { - return impl->GenericJson("DELETE", path, data, allow_anonymous); + return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json"); +} + +Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { + return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain"); +} + +Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { + return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png"); +} + +Common::WebResult Client::GetExternalJWT(const std::string& audience) { + return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false, + "text/html"); } } // namespace WebService diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index c637e09df..04121f17e 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h @@ -46,6 +46,29 @@ public: Common::WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous); + /** + * Gets a plain string from the specified path. + * @param path the URL segment after the host address. + * @param allow_anonymous If true, allow anonymous unauthenticated requests. + * @return the result of the request. + */ + Common::WebResult GetPlain(const std::string& path, bool allow_anonymous); + + /** + * Gets an PNG image from the specified path. + * @param path the URL segment after the host address. + * @param allow_anonymous If true, allow anonymous unauthenticated requests. + * @return the result of the request. + */ + Common::WebResult GetImage(const std::string& path, bool allow_anonymous); + + /** + * Requests an external JWT for the specific audience provided. + * @param audience the audience of the JWT requested. + * @return the result of the request. + */ + Common::WebResult GetExternalJWT(const std::string& audience); + private: struct Impl; std::unique_ptr<Impl> impl; diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index 336b062b3..8637f5b3c 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -11,6 +11,31 @@ #include "yuzu/configuration/configure_web.h" #include "yuzu/uisettings.h" +static constexpr char token_delimiter{':'}; + +static std::string GenerateDisplayToken(const std::string& username, const std::string& token) { + if (username.empty() || token.empty()) { + return {}; + } + + const std::string unencoded_display_token{username + token_delimiter + token}; + QByteArray b{unencoded_display_token.c_str()}; + QByteArray b64 = b.toBase64(); + return b64.toStdString(); +} + +static std::string UsernameFromDisplayToken(const std::string& display_token) { + const std::string unencoded_display_token{ + QByteArray::fromBase64(display_token.c_str()).toStdString()}; + return unencoded_display_token.substr(0, unencoded_display_token.find(token_delimiter)); +} + +static std::string TokenFromDisplayToken(const std::string& display_token) { + const std::string unencoded_display_token{ + QByteArray::fromBase64(display_token.c_str()).toStdString()}; + return unencoded_display_token.substr(unencoded_display_token.find(token_delimiter) + 1); +} + ConfigureWeb::ConfigureWeb(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) { ui->setupUi(this); @@ -63,13 +88,18 @@ void ConfigureWeb::SetConfiguration() { ui->web_signup_link->setOpenExternalLinks(true); ui->web_token_info_link->setOpenExternalLinks(true); + if (Settings::values.yuzu_username.empty()) { + ui->username->setText(tr("Unspecified")); + } else { + ui->username->setText(QString::fromStdString(Settings::values.yuzu_username)); + } + ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); - ui->edit_username->setText(QString::fromStdString(Settings::values.yuzu_username)); - ui->edit_token->setText(QString::fromStdString(Settings::values.yuzu_token)); + ui->edit_token->setText(QString::fromStdString( + GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token))); // Connect after setting the values, to avoid calling OnLoginChanged now connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); - connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); user_verified = true; @@ -80,12 +110,13 @@ void ConfigureWeb::ApplyConfiguration() { Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked(); if (user_verified) { - Settings::values.yuzu_username = ui->edit_username->text().toStdString(); - Settings::values.yuzu_token = ui->edit_token->text().toStdString(); + Settings::values.yuzu_username = + UsernameFromDisplayToken(ui->edit_token->text().toStdString()); + Settings::values.yuzu_token = TokenFromDisplayToken(ui->edit_token->text().toStdString()); } else { - QMessageBox::warning(this, tr("Username and token not verified"), - tr("Username and token were not verified. The changes to your " - "username and/or token have not been saved.")); + QMessageBox::warning( + this, tr("Token not verified"), + tr("Token was not verified. The change to your token has not been saved.")); } } @@ -96,17 +127,15 @@ void ConfigureWeb::RefreshTelemetryID() { } void ConfigureWeb::OnLoginChanged() { - if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) { + if (ui->edit_token->text().isEmpty()) { user_verified = true; const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); - ui->label_username_verified->setPixmap(pixmap); ui->label_token_verified->setPixmap(pixmap); } else { user_verified = false; const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); - ui->label_username_verified->setPixmap(pixmap); ui->label_token_verified->setPixmap(pixmap); } } @@ -114,10 +143,11 @@ void ConfigureWeb::OnLoginChanged() { void ConfigureWeb::VerifyLogin() { ui->button_verify_login->setDisabled(true); ui->button_verify_login->setText(tr("Verifying...")); - verify_watcher.setFuture(QtConcurrent::run([username = ui->edit_username->text().toStdString(), - token = ui->edit_token->text().toStdString()] { - return Core::VerifyLogin(username, token); - })); + verify_watcher.setFuture(QtConcurrent::run( + [username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()), + token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] { + return Core::VerifyLogin(username, token); + })); } void ConfigureWeb::OnLoginVerified() { @@ -127,16 +157,15 @@ void ConfigureWeb::OnLoginVerified() { user_verified = true; const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); - ui->label_username_verified->setPixmap(pixmap); ui->label_token_verified->setPixmap(pixmap); + ui->username->setText( + QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString()))); } else { const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); - ui->label_username_verified->setPixmap(pixmap); ui->label_token_verified->setPixmap(pixmap); - - QMessageBox::critical( - this, tr("Verification failed"), - tr("Verification failed. Check that you have entered your username and token " - "correctly, and that your internet connection is working.")); + ui->username->setText(tr("Unspecified")); + QMessageBox::critical(this, tr("Verification failed"), + tr("Verification failed. Check that you have entered your token " + "correctly, and that your internet connection is working.")); } } diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui index 2f4b9dd73..8c07d1165 100644 --- a/src/yuzu/configuration/configure_web.ui +++ b/src/yuzu/configuration/configure_web.ui @@ -55,11 +55,7 @@ </widget> </item> <item row="0" column="1" colspan="3"> - <widget class="QLineEdit" name="edit_username"> - <property name="maxLength"> - <number>36</number> - </property> - </widget> + <widget class="QLabel" name="username" /> </item> <item row="1" column="0"> <widget class="QLabel" name="label_token"> @@ -79,14 +75,10 @@ </property> </widget> </item> - <item row="0" column="4"> - <widget class="QLabel" name="label_username_verified"> - </widget> - </item> <item row="1" column="1" colspan="3"> <widget class="QLineEdit" name="edit_token"> <property name="maxLength"> - <number>36</number> + <number>80</number> </property> <property name="echoMode"> <enum>QLineEdit::Password</enum> |