diff options
25 files changed, 474 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 79d8046d9..ddba04ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,11 +295,18 @@ function(create_directory_groups)      endforeach()  endfunction() -# generate git revision information +# Gets a UTC timstamp and sets the provided variable to it +function(get_timestamp _var) +    string(TIMESTAMP timestamp UTC) +    set(${_var} "${timestamp}" PARENT_SCOPE) +endfunction() + +# generate git/build information  include(GetGitRevisionDescription)  get_git_head_revision(GIT_REF_SPEC GIT_REV)  git_describe(GIT_DESC --always --long --dirty)  git_branch_name(GIT_BRANCH) +get_timestamp(BUILD_DATE)  enable_testing()  add_subdirectory(externals) diff --git a/externals/cryptopp/cryptopp b/externals/cryptopp/cryptopp -Subproject 841c37e34765487a2968357369ab74db8b10a62 +Subproject 24bc2b85674254fb294e717eb5b47d9f53e786b diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 02bfdca3d..c1ae0ccc8 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -311,7 +311,7 @@ bool GMainWindow::LoadROM(const QString& filename) {      if (!gladLoadGL()) {          QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"), -                              tr("Your GPU may not support OpenGL 3.3, or you do not" +                              tr("Your GPU may not support OpenGL 3.3, or you do not "                                   "have the latest graphics driver."));          return false;      } diff --git a/src/common/quaternion.h b/src/common/quaternion.h index 84ac82ed3..77f626bcb 100644 --- a/src/common/quaternion.h +++ b/src/common/quaternion.h @@ -30,6 +30,11 @@ public:          return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),                  w * other.w - Dot(xyz, other.xyz)};      } + +    Quaternion<T> Normalized() const { +        T length = std::sqrt(xyz.Length2() + w * w); +        return {xyz / length, w / length}; +    }  };  template <typename T> diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 0080db5d5..4083095d5 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -8,6 +8,7 @@  #define GIT_BRANCH   "@GIT_BRANCH@"  #define GIT_DESC     "@GIT_DESC@"  #define BUILD_NAME   "@REPO_NAME@" +#define BUILD_DATE   "@BUILD_DATE@"  namespace Common { @@ -15,6 +16,7 @@ const char g_scm_rev[]      = GIT_REV;  const char g_scm_branch[]   = GIT_BRANCH;  const char g_scm_desc[]     = GIT_DESC;  const char g_build_name[]   = BUILD_NAME; +const char g_build_date[]   = BUILD_DATE;  } // namespace diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index e22389803..18aaa1735 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -10,5 +10,6 @@ extern const char g_scm_rev[];  extern const char g_scm_branch[];  extern const char g_scm_desc[];  extern const char g_build_name[]; +extern const char g_build_date[];  } // namespace diff --git a/src/common/vector_math.h b/src/common/vector_math.h index c7a461a1e..6e2a5ad60 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -31,7 +31,6 @@  #pragma once  #include <cmath> -#include <type_traits>  namespace Math { @@ -90,7 +89,7 @@ public:          x -= other.x;          y -= other.y;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec2<decltype(-T{})> operator-() const {          return MakeVec(-x, -y);      } @@ -247,7 +246,7 @@ public:          y -= other.y;          z -= other.z;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec3<decltype(-T{})> operator-() const {          return MakeVec(-x, -y, -z);      } @@ -462,7 +461,7 @@ public:          z -= other.z;          w -= other.w;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec4<decltype(-T{})> operator-() const {          return MakeVec(-x, -y, -z, -w);      } diff --git a/src/core/core.h b/src/core/core.h index 4e3b6b409..9805cc694 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,6 +7,7 @@  #include <memory>  #include <string>  #include "common/common_types.h" +#include "core/loader/loader.h"  #include "core/memory.h"  #include "core/perf_stats.h"  #include "core/telemetry_session.h" @@ -14,10 +15,6 @@  class EmuWindow;  class ARM_Interface; -namespace Loader { -class AppLoader; -} -  namespace Core {  class System { @@ -119,6 +116,10 @@ public:          return status_details;      } +    Loader::AppLoader& GetAppLoader() const { +        return *app_loader; +    } +  private:      /**       * Initialize the emulated system. diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 4e6b7b6f5..0109fa2b2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -75,6 +75,10 @@ void Initialize(Service::Interface* self) {  void GetSharedFont(Service::Interface* self) {      IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000      IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + +    // Log in telemetry if the game uses the shared font +    Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); +      if (!shared_font_loaded) {          LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");          rb.Push<u32>(-1); // TODO: Find the right error code diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 6624f1711..3dbeb27cc 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {      CryptoPP::AutoSeededRandomPool rng;      random_number = rng.GenerateWord32(0, 0xFFFF);      u64_le local_friend_code_seed; -    rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed), +    rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),                        sizeof(local_friend_code_seed));      console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);  } diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp index 56f934b3f..6f2bf2061 100644 --- a/src/core/hle/service/dlp/dlp_clnt.cpp +++ b/src/core/hle/service/dlp/dlp_clnt.cpp @@ -8,7 +8,26 @@ namespace Service {  namespace DLP {  const Interface::FunctionInfo FunctionTable[] = { -    {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x000100C3, nullptr, "Initialize"}, +    {0x00020000, nullptr, "Finalize"}, +    {0x00030000, nullptr, "GetEventDesc"}, +    {0x00040000, nullptr, "GetChannel"}, +    {0x00050180, nullptr, "StartScan"}, +    {0x00060000, nullptr, "StopScan"}, +    {0x00070080, nullptr, "GetServerInfo"}, +    {0x00080100, nullptr, "GetTitleInfo"}, +    {0x00090040, nullptr, "GetTitleInfoInOrder"}, +    {0x000A0080, nullptr, "DeleteScanInfo"}, +    {0x000B0100, nullptr, "PrepareForSystemDownload"}, +    {0x000C0000, nullptr, "StartSystemDownload"}, +    {0x000D0100, nullptr, "StartTitleDownload"}, +    {0x000E0000, nullptr, "GetMyStatus"}, +    {0x000F0040, nullptr, "GetConnectingNodes"}, +    {0x00100040, nullptr, "GetNodeInfo"}, +    {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00120000, nullptr, "StopSession"}, +    {0x00130100, nullptr, "GetCupVersion"}, +    {0x00140100, nullptr, "GetDupAvailability"},  };  DLP_CLNT_Interface::DLP_CLNT_Interface() { diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp index 29b9d52e0..fe6be7d32 100644 --- a/src/core/hle/service/dlp/dlp_fkcl.cpp +++ b/src/core/hle/service/dlp/dlp_fkcl.cpp @@ -8,7 +8,23 @@ namespace Service {  namespace DLP {  const Interface::FunctionInfo FunctionTable[] = { -    {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00010083, nullptr, "Initialize"}, +    {0x00020000, nullptr, "Finalize"}, +    {0x00030000, nullptr, "GetEventDesc"}, +    {0x00040000, nullptr, "GetChannels"}, +    {0x00050180, nullptr, "StartScan"}, +    {0x00060000, nullptr, "StopScan"}, +    {0x00070080, nullptr, "GetServerInfo"}, +    {0x00080100, nullptr, "GetTitleInfo"}, +    {0x00090040, nullptr, "GetTitleInfoInOrder"}, +    {0x000A0080, nullptr, "DeleteScanInfo"}, +    {0x000B0100, nullptr, "StartFakeSession"}, +    {0x000C0000, nullptr, "GetMyStatus"}, +    {0x000D0040, nullptr, "GetConnectingNodes"}, +    {0x000E0040, nullptr, "GetNodeInfo"}, +    {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00100000, nullptr, "StopSession"}, +    {0x00110203, nullptr, "Initialize2"},  };  DLP_FKCL_Interface::DLP_FKCL_Interface() { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 32cfa2c44..1bcea43d3 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -11,7 +11,7 @@  namespace Service {  namespace DLP { -static void unk_0x000E0040(Interface* self) { +static void IsChild(Interface* self) {      u32* cmd_buff = Kernel::GetCommandBuffer();      cmd_buff[1] = RESULT_SUCCESS.raw; @@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {      {0x00010183, nullptr, "Initialize"},      {0x00020000, nullptr, "Finalize"},      {0x00030000, nullptr, "GetServerState"}, +    {0x00040000, nullptr, "GetEventDescription"},      {0x00050080, nullptr, "StartAccepting"}, +    {0x00060000, nullptr, "EndAccepting"},      {0x00070000, nullptr, "StartDistribution"},      {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},      {0x00090040, nullptr, "AcceptClient"}, +    {0x000A0040, nullptr, "DisconnectClient"},      {0x000B0042, nullptr, "GetConnectingClients"},      {0x000C0040, nullptr, "GetClientInfo"},      {0x000D0040, nullptr, "GetClientState"}, -    {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, +    {0x000E0040, IsChild, "IsChild"}, +    {0x000F0303, nullptr, "InitializeWithName"}, +    {0x00100000, nullptr, "GetDupNoticeNeed"},  };  DLP_SRVR_Interface::DLP_SRVR_Interface() { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 48bbf687d..e731888a2 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -166,6 +166,15 @@ public:          return ResultStatus::ErrorNotImplemented;      } +    /** +     * Get the title of the application +     * @param title Reference to store the application title into +     * @return ResultStatus result of function +     */ +    virtual ResultStatus ReadTitle(std::string& title) { +        return ResultStatus::ErrorNotImplemented; +    } +  protected:      FileUtil::IOFile file;      bool is_loaded = false; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index fc4d14a59..c007069a9 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -4,7 +4,9 @@  #include <algorithm>  #include <cinttypes> +#include <codecvt>  #include <cstring> +#include <locale>  #include <memory>  #include "common/logging/log.h"  #include "common/string_util.h" @@ -420,4 +422,22 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_      return ResultStatus::ErrorNotUsed;  } +ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) { +    std::vector<u8> data; +    Loader::SMDH smdh; +    ReadIcon(data); + +    if (!Loader::IsValidSMDH(data)) { +        return ResultStatus::ErrorInvalidFormat; +    } + +    memcpy(&smdh, data.data(), sizeof(Loader::SMDH)); + +    const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English); +    auto title_end = std::find(short_title.begin(), short_title.end(), u'\0'); +    title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end}); + +    return ResultStatus::Success; +} +  } // namespace Loader diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 0ebd47fd5..e40cef764 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -191,23 +191,13 @@ public:      ResultStatus ReadLogo(std::vector<u8>& buffer) override; -    /** -     * Get the program id of the application -     * @param out_program_id Reference to store program id into -     * @return ResultStatus result of function -     */      ResultStatus ReadProgramId(u64& out_program_id) override; -    /** -     * Get the RomFS of the application -     * @param romfs_file Reference to buffer to store data -     * @param offset     Offset in the file to the RomFS -     * @param size       Size of the RomFS in bytes -     * @return ResultStatus result of function -     */      ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,                             u64& size) override; +    ResultStatus ReadTitle(std::string& title) override; +  private:      /**       * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 841d6cfa1..94483f385 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -7,6 +7,7 @@  #include "common/assert.h"  #include "common/scm_rev.h"  #include "common/x64/cpu_detect.h" +#include "core/core.h"  #include "core/settings.h"  #include "core/telemetry_session.h" @@ -39,12 +40,19 @@ TelemetrySession::TelemetrySession() {                              std::chrono::system_clock::now().time_since_epoch())                              .count()};      AddField(Telemetry::FieldType::Session, "Init_Time", init_time); +    std::string program_name; +    const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)}; +    if (res == Loader::ResultStatus::Success) { +        AddField(Telemetry::FieldType::Session, "ProgramName", program_name); +    }      // Log application information      const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};      AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty);      AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch);      AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev); +    AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date); +    AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name);      // Log user system information      AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); @@ -68,6 +76,15 @@ TelemetrySession::TelemetrySession() {               Common::GetCPUCaps().sse4_1);      AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42",               Common::GetCPUCaps().sse4_2); +#ifdef __APPLE__ +    AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple"); +#elif defined(_WIN32) +    AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Windows"); +#elif defined(__linux__) || defined(linux) || defined(__linux) +    AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Linux"); +#else +    AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Unknown"); +#endif      // Log user configuration information      AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 0961a3251..cffa4c952 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -15,6 +15,7 @@ set(SRCS              shader/shader_interpreter.cpp              swrasterizer/clipper.cpp              swrasterizer/framebuffer.cpp +            swrasterizer/lighting.cpp              swrasterizer/proctex.cpp              swrasterizer/rasterizer.cpp              swrasterizer/swrasterizer.cpp @@ -55,6 +56,7 @@ set(HEADERS              shader/shader_interpreter.h              swrasterizer/clipper.h              swrasterizer/framebuffer.h +            swrasterizer/lighting.h              swrasterizer/proctex.h              swrasterizer/rasterizer.h              swrasterizer/swrasterizer.h diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 4633a1df1..f98ca3302 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -119,27 +119,6 @@ static void WriteUniformFloatReg(ShaderRegs& config, Shader::ShaderSetup& setup,      }  } -static void WriteProgramCode(ShaderRegs& config, Shader::ShaderSetup& setup, -                             unsigned max_program_code_length, u32 value) { -    if (config.program.offset >= max_program_code_length) { -        LOG_ERROR(HW_GPU, "Invalid %s program offset %d", GetShaderSetupTypeName(setup), -                  (int)config.program.offset); -    } else { -        setup.program_code[config.program.offset] = value; -        config.program.offset++; -    } -} - -static void WriteSwizzlePatterns(ShaderRegs& config, Shader::ShaderSetup& setup, u32 value) { -    if (config.swizzle_patterns.offset >= setup.swizzle_data.size()) { -        LOG_ERROR(HW_GPU, "Invalid %s swizzle pattern offset %d", GetShaderSetupTypeName(setup), -                  (int)config.swizzle_patterns.offset); -    } else { -        setup.swizzle_data[config.swizzle_patterns.offset] = value; -        config.swizzle_patterns.offset++; -    } -} -  static void WritePicaReg(u32 id, u32 value, u32 mask) {      auto& regs = g_state.regs; @@ -458,7 +437,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[5], 0x2a1):      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[6], 0x2a2):      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[7], 0x2a3): { -        WriteProgramCode(g_state.regs.gs, g_state.gs, 4096, value); +        u32& offset = g_state.regs.gs.program.offset; +        if (offset >= 4096) { +            LOG_ERROR(HW_GPU, "Invalid GS program offset %u", offset); +        } else { +            g_state.gs.program_code[offset] = value; +            offset++; +        }          break;      } @@ -470,11 +455,18 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[5], 0x2ab):      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[6], 0x2ac):      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[7], 0x2ad): { -        WriteSwizzlePatterns(g_state.regs.gs, g_state.gs, value); +        u32& offset = g_state.regs.gs.swizzle_patterns.offset; +        if (offset >= g_state.gs.swizzle_data.size()) { +            LOG_ERROR(HW_GPU, "Invalid GS swizzle pattern offset %u", offset); +        } else { +            g_state.gs.swizzle_data[offset] = value; +            offset++; +        }          break;      }      case PICA_REG_INDEX(vs.bool_uniforms): +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          WriteUniformBoolReg(g_state.vs, g_state.regs.vs.bool_uniforms.Value());          break; @@ -482,6 +474,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2):      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3):      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4): { +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          unsigned index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1));          auto values = regs.vs.int_uniforms[index];          WriteUniformIntReg(g_state.vs, index, @@ -497,6 +490,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6):      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7):      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8): { +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          WriteUniformFloatReg(g_state.regs.vs, g_state.vs, vs_float_regs_counter,                               vs_uniform_write_buffer, value);          break; @@ -510,7 +504,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1):      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2):      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3): { -        WriteProgramCode(g_state.regs.vs, g_state.vs, 512, value); +        u32& offset = g_state.regs.vs.program.offset; +        if (offset >= 512) { +            LOG_ERROR(HW_GPU, "Invalid VS program offset %u", offset); +        } else { +            g_state.vs.program_code[offset] = value; +            if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) { +                g_state.gs.program_code[offset] = value; +            } +            offset++; +        }          break;      } @@ -522,7 +525,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db):      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc):      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd): { -        WriteSwizzlePatterns(g_state.regs.vs, g_state.vs, value); +        u32& offset = g_state.regs.vs.swizzle_patterns.offset; +        if (offset >= g_state.vs.swizzle_data.size()) { +            LOG_ERROR(HW_GPU, "Invalid VS swizzle pattern offset %u", offset); +        } else { +            g_state.vs.swizzle_data[offset] = value; +            if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) { +                g_state.gs.swizzle_data[offset] = value; +            } +            offset++; +        }          break;      } diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index 2d23d34e6..864a2c9e6 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h @@ -79,7 +79,7 @@ struct State {          std::array<ColorDifferenceEntry, 256> color_diff_table;      } proctex; -    struct { +    struct Lighting {          union LutEntry {              // Used for raw access              u32 raw; diff --git a/src/video_core/regs_pipeline.h b/src/video_core/regs_pipeline.h index 31c747d77..8b6369297 100644 --- a/src/video_core/regs_pipeline.h +++ b/src/video_core/regs_pipeline.h @@ -202,7 +202,14 @@ struct PipelineRegs {      /// Number of input attributes to the vertex shader minus 1      BitField<0, 4, u32> max_input_attrib_index; -    INSERT_PADDING_WORDS(2); +    INSERT_PADDING_WORDS(1); + +    // The shader unit 3, which can be used for both vertex and geometry shader, gets its +    // configuration depending on this register. If this is not set, unit 3 will share some +    // configuration with other units. It is known that program code and swizzle pattern uploaded +    // via regs.vs will be also uploaded to unit 3 if this is not set. Although very likely, it is +    // still unclear whether uniforms and other configuration can be also shared. +    BitField<0, 1, u32> gs_unit_exclusive_configuration;      enum class GPUMode : u32 {          Drawing = 0, diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp index 6fb923756..7537689b7 100644 --- a/src/video_core/swrasterizer/clipper.cpp +++ b/src/video_core/swrasterizer/clipper.cpp @@ -95,6 +95,17 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu      static const size_t MAX_VERTICES = 9;      static_vector<Vertex, MAX_VERTICES> buffer_a = {v0, v1, v2};      static_vector<Vertex, MAX_VERTICES> buffer_b; + +    auto FlipQuaternionIfOpposite = [](auto& a, const auto& b) { +        if (Math::Dot(a, b) < float24::Zero()) +            a = -a; +    }; + +    // Flip the quaternions if they are opposite to prevent interpolating them over the wrong +    // direction. +    FlipQuaternionIfOpposite(buffer_a[1].quat, buffer_a[0].quat); +    FlipQuaternionIfOpposite(buffer_a[2].quat, buffer_a[0].quat); +      auto* output_list = &buffer_a;      auto* input_list = &buffer_b; diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp new file mode 100644 index 000000000..63088eee8 --- /dev/null +++ b/src/video_core/swrasterizer/lighting.cpp @@ -0,0 +1,250 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/math_util.h" +#include "video_core/swrasterizer/lighting.h" + +namespace Pica { + +static float LookupLightingLut(const Pica::State::Lighting& lighting, size_t lut_index, u8 index, +                               float delta) { +    ASSERT_MSG(lut_index < lighting.luts.size(), "Out of range lut"); +    ASSERT_MSG(index < lighting.luts[lut_index].size(), "Out of range index"); + +    const auto& lut = lighting.luts[lut_index][index]; + +    float lut_value = lut.ToFloat(); +    float lut_diff = lut.DiffToFloat(); + +    return lut_value + lut_diff * delta; +} + +std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( +    const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state, +    const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view) { + +    // TODO(Subv): Bump mapping +    Math::Vec3<float> surface_normal = {0.0f, 0.0f, 1.0f}; + +    if (lighting.config0.bump_mode != LightingRegs::LightingBumpMode::None) { +        LOG_CRITICAL(HW_GPU, "unimplemented bump mapping"); +        UNIMPLEMENTED(); +    } + +    // Use the normalized the quaternion when performing the rotation +    auto normal = Math::QuaternionRotate(normquat, surface_normal); + +    Math::Vec4<float> diffuse_sum = {0.0f, 0.0f, 0.0f, 1.0f}; +    Math::Vec4<float> specular_sum = {0.0f, 0.0f, 0.0f, 1.0f}; + +    for (unsigned light_index = 0; light_index <= lighting.max_light_index; ++light_index) { +        unsigned num = lighting.light_enable.GetNum(light_index); +        const auto& light_config = lighting.light[num]; + +        Math::Vec3<float> refl_value = {}; +        Math::Vec3<float> position = {float16::FromRaw(light_config.x).ToFloat32(), +                                      float16::FromRaw(light_config.y).ToFloat32(), +                                      float16::FromRaw(light_config.z).ToFloat32()}; +        Math::Vec3<float> light_vector; + +        if (light_config.config.directional) +            light_vector = position; +        else +            light_vector = position + view; + +        light_vector.Normalize(); + +        float dist_atten = 1.0f; +        if (!lighting.IsDistAttenDisabled(num)) { +            auto distance = (-view - position).Length(); +            float scale = Pica::float20::FromRaw(light_config.dist_atten_scale).ToFloat32(); +            float bias = Pica::float20::FromRaw(light_config.dist_atten_bias).ToFloat32(); +            size_t lut = +                static_cast<size_t>(LightingRegs::LightingSampler::DistanceAttenuation) + num; + +            float sample_loc = MathUtil::Clamp(scale * distance + bias, 0.0f, 1.0f); + +            u8 lutindex = +                static_cast<u8>(MathUtil::Clamp(std::floor(sample_loc * 256.0f), 0.0f, 255.0f)); +            float delta = sample_loc * 256 - lutindex; +            dist_atten = LookupLightingLut(lighting_state, lut, lutindex, delta); +        } + +        auto GetLutValue = [&](LightingRegs::LightingLutInput input, bool abs, +                               LightingRegs::LightingScale scale_enum, +                               LightingRegs::LightingSampler sampler) { +            Math::Vec3<float> norm_view = view.Normalized(); +            Math::Vec3<float> half_angle = (norm_view + light_vector).Normalized(); +            float result = 0.0f; + +            switch (input) { +            case LightingRegs::LightingLutInput::NH: +                result = Math::Dot(normal, half_angle); +                break; + +            case LightingRegs::LightingLutInput::VH: +                result = Math::Dot(norm_view, half_angle); +                break; + +            case LightingRegs::LightingLutInput::NV: +                result = Math::Dot(normal, norm_view); +                break; + +            case LightingRegs::LightingLutInput::LN: +                result = Math::Dot(light_vector, normal); +                break; + +            default: +                LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input)); +                UNIMPLEMENTED(); +                result = 0.0f; +            } + +            u8 index; +            float delta; + +            if (abs) { +                if (light_config.config.two_sided_diffuse) +                    result = std::abs(result); +                else +                    result = std::max(result, 0.0f); + +                float flr = std::floor(result * 256.0f); +                index = static_cast<u8>(MathUtil::Clamp(flr, 0.0f, 255.0f)); +                delta = result * 256 - index; +            } else { +                float flr = std::floor(result * 128.0f); +                s8 signed_index = static_cast<s8>(MathUtil::Clamp(flr, -128.0f, 127.0f)); +                delta = result * 128.0f - signed_index; +                index = static_cast<u8>(signed_index); +            } + +            float scale = lighting.lut_scale.GetScale(scale_enum); +            return scale * +                   LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta); +        }; + +        // Specular 0 component +        float d0_lut_value = 1.0f; +        if (lighting.config1.disable_lut_d0 == 0 && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config0.config, LightingRegs::LightingSampler::Distribution0)) { +            d0_lut_value = +                GetLutValue(lighting.lut_input.d0, lighting.abs_lut_input.disable_d0 == 0, +                            lighting.lut_scale.d0, LightingRegs::LightingSampler::Distribution0); +        } + +        Math::Vec3<float> specular_0 = d0_lut_value * light_config.specular_0.ToVec3f(); + +        // If enabled, lookup ReflectRed value, otherwise, 1.0 is used +        if (lighting.config1.disable_lut_rr == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectRed)) { +            refl_value.x = +                GetLutValue(lighting.lut_input.rr, lighting.abs_lut_input.disable_rr == 0, +                            lighting.lut_scale.rr, LightingRegs::LightingSampler::ReflectRed); +        } else { +            refl_value.x = 1.0f; +        } + +        // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used +        if (lighting.config1.disable_lut_rg == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectGreen)) { +            refl_value.y = +                GetLutValue(lighting.lut_input.rg, lighting.abs_lut_input.disable_rg == 0, +                            lighting.lut_scale.rg, LightingRegs::LightingSampler::ReflectGreen); +        } else { +            refl_value.y = refl_value.x; +        } + +        // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used +        if (lighting.config1.disable_lut_rb == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectBlue)) { +            refl_value.z = +                GetLutValue(lighting.lut_input.rb, lighting.abs_lut_input.disable_rb == 0, +                            lighting.lut_scale.rb, LightingRegs::LightingSampler::ReflectBlue); +        } else { +            refl_value.z = refl_value.x; +        } + +        // Specular 1 component +        float d1_lut_value = 1.0f; +        if (lighting.config1.disable_lut_d1 == 0 && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config0.config, LightingRegs::LightingSampler::Distribution1)) { +            d1_lut_value = +                GetLutValue(lighting.lut_input.d1, lighting.abs_lut_input.disable_d1 == 0, +                            lighting.lut_scale.d1, LightingRegs::LightingSampler::Distribution1); +        } + +        Math::Vec3<float> specular_1 = +            d1_lut_value * refl_value * light_config.specular_1.ToVec3f(); + +        // Fresnel +        if (lighting.config1.disable_lut_fr == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::Fresnel)) { + +            float lut_value = +                GetLutValue(lighting.lut_input.fr, lighting.abs_lut_input.disable_fr == 0, +                            lighting.lut_scale.fr, LightingRegs::LightingSampler::Fresnel); + +            // Enabled for diffuse lighting alpha component +            if (lighting.config0.fresnel_selector == +                    LightingRegs::LightingFresnelSelector::PrimaryAlpha || +                lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { +                diffuse_sum.a() *= lut_value; +            } + +            // Enabled for the specular lighting alpha component +            if (lighting.config0.fresnel_selector == +                    LightingRegs::LightingFresnelSelector::SecondaryAlpha || +                lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { +                specular_sum.a() *= lut_value; +            } +        } + +        auto dot_product = Math::Dot(light_vector, normal); + +        // Calculate clamp highlights before applying the two-sided diffuse configuration to the dot +        // product. +        float clamp_highlights = 1.0f; +        if (lighting.config0.clamp_highlights) { +            if (dot_product <= 0.0f) +                clamp_highlights = 0.0f; +            else +                clamp_highlights = 1.0f; +        } + +        if (light_config.config.two_sided_diffuse) +            dot_product = std::abs(dot_product); +        else +            dot_product = std::max(dot_product, 0.0f); + +        auto diffuse = +            light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); +        diffuse_sum += Math::MakeVec(diffuse * dist_atten, 0.0f); + +        specular_sum += +            Math::MakeVec((specular_0 + specular_1) * clamp_highlights * dist_atten, 0.0f); +    } + +    diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); + +    auto diffuse = Math::MakeVec<float>(MathUtil::Clamp(diffuse_sum.x, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.y, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.z, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.w, 0.0f, 1.0f) * 255) +                       .Cast<u8>(); +    auto specular = Math::MakeVec<float>(MathUtil::Clamp(specular_sum.x, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.y, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.z, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.w, 0.0f, 1.0f) * 255) +                        .Cast<u8>(); +    return {diffuse, specular}; +} + +} // namespace Pica diff --git a/src/video_core/swrasterizer/lighting.h b/src/video_core/swrasterizer/lighting.h new file mode 100644 index 000000000..438dca926 --- /dev/null +++ b/src/video_core/swrasterizer/lighting.h @@ -0,0 +1,18 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <tuple> +#include "common/quaternion.h" +#include "common/vector_math.h" +#include "video_core/pica_state.h" + +namespace Pica { + +std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( +    const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state, +    const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view); + +} // namespace Pica diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 512e81c08..fdc1df199 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp @@ -13,6 +13,7 @@  #include "common/logging/log.h"  #include "common/math_util.h"  #include "common/microprofile.h" +#include "common/quaternion.h"  #include "common/vector_math.h"  #include "core/hw/gpu.h"  #include "core/memory.h" @@ -24,6 +25,7 @@  #include "video_core/regs_texturing.h"  #include "video_core/shader/shader.h"  #include "video_core/swrasterizer/framebuffer.h" +#include "video_core/swrasterizer/lighting.h"  #include "video_core/swrasterizer/proctex.h"  #include "video_core/swrasterizer/rasterizer.h"  #include "video_core/swrasterizer/texturing.h" @@ -419,6 +421,26 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve                  regs.texturing.tev_combiner_buffer_color.a,              }; +            Math::Vec4<u8> primary_fragment_color = {0, 0, 0, 0}; +            Math::Vec4<u8> secondary_fragment_color = {0, 0, 0, 0}; + +            if (!g_state.regs.lighting.disable) { +                Math::Quaternion<float> normquat = Math::Quaternion<float>{ +                    {GetInterpolatedAttribute(v0.quat.x, v1.quat.x, v2.quat.x).ToFloat32(), +                     GetInterpolatedAttribute(v0.quat.y, v1.quat.y, v2.quat.y).ToFloat32(), +                     GetInterpolatedAttribute(v0.quat.z, v1.quat.z, v2.quat.z).ToFloat32()}, +                    GetInterpolatedAttribute(v0.quat.w, v1.quat.w, v2.quat.w).ToFloat32(), +                }.Normalized(); + +                Math::Vec3<float> view{ +                    GetInterpolatedAttribute(v0.view.x, v1.view.x, v2.view.x).ToFloat32(), +                    GetInterpolatedAttribute(v0.view.y, v1.view.y, v2.view.y).ToFloat32(), +                    GetInterpolatedAttribute(v0.view.z, v1.view.z, v2.view.z).ToFloat32(), +                }; +                std::tie(primary_fragment_color, secondary_fragment_color) = +                    ComputeFragmentsColors(g_state.regs.lighting, g_state.lighting, normquat, view); +            } +              for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size();                   ++tev_stage_index) {                  const auto& tev_stage = tev_stages[tev_stage_index]; @@ -427,14 +449,13 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve                  auto GetSource = [&](Source source) -> Math::Vec4<u8> {                      switch (source) {                      case Source::PrimaryColor: +                        return primary_color; -                    // HACK: Until we implement fragment lighting, use primary_color                      case Source::PrimaryFragmentColor: -                        return primary_color; +                        return primary_fragment_color; -                    // HACK: Until we implement fragment lighting, use zero                      case Source::SecondaryFragmentColor: -                        return {0, 0, 0, 0}; +                        return secondary_fragment_color;                      case Source::Texture0:                          return texture_color[0];  | 
