diff --git a/.travis/common/travis-ci.env b/.travis/common/travis-ci.env new file mode 100644 index 000000000..7d63674b4 --- /dev/null +++ b/.travis/common/travis-ci.env @@ -0,0 +1,15 @@ +# List of environment variables to be shared with Docker containers +CI +TRAVIS +CONTINUOUS_INTEGRATION +TRAVIS_BRANCH +TRAVIS_BUILD_ID +TRAVIS_BUILD_NUMBER +TRAVIS_COMMIT +TRAVIS_JOB_ID +TRAVIS_JOB_NUMBER +TRAVIS_REPO_SLUG +TRAVIS_TAG + +# citra specific flags +ENABLE_COMPATIBILITY_REPORTING diff --git a/.travis/linux-frozen/build.sh b/.travis/linux-frozen/build.sh index a6372bd33..d849ceaf2 100755 --- a/.travis/linux-frozen/build.sh +++ b/.travis/linux-frozen/build.sh @@ -1,4 +1,4 @@ #!/bin/bash -ex mkdir -p "$HOME/.ccache" docker pull ubuntu:18.04 -docker run -e ENABLE_COMPATIBILITY_REPORTING -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh +docker run --env-file .travis/common/travis-ci.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 53e8fa610..499d15b1b 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -ex mkdir -p "$HOME/.ccache" -docker run -e ENABLE_COMPATIBILITY_REPORTING -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux/docker.sh +docker run --env-file .travis/common/travis-ci.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux/docker.sh diff --git a/.travis/transifex/docker.sh b/.travis/transifex/docker.sh index 9ca97e222..88913ba9e 100644 --- a/.travis/transifex/docker.sh +++ b/.travis/transifex/docker.sh @@ -9,7 +9,21 @@ echo -e "\e[1m\e[33mInstalling dependencies...\e[0m" apk update apk add build-base cmake python3-dev qt5-qttools-dev qt5-qtmultimedia-dev -pip3 install transifex-client +pip3 install --upgrade pip transifex-client + +cat << 'EOF' > /usr/bin/tx +#!/usr/bin/python3 + +# -*- coding: utf-8 -*- +import re +import sys + +from txclib.cmdline import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) +EOF echo -e "\e[1m\e[33mBuild tools information:\e[0m" cmake --version diff --git a/appveyor.yml b/appveyor.yml index a6e66881d..772635267 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,13 +27,14 @@ install: - ps: | if ($env:BUILD_TYPE -eq 'mingw') { $dependencies = "mingw64/mingw-w64-x86_64-qt5" + C:\msys64\usr\bin\bash -lc "pacman -Syy" # redirect err to null to prevent warnings from becoming errors # workaround to prevent pacman from failing due to cyclical dependencies C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-SDL2-2.0.5-2-any.pkg.tar.xz" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null - # stick to cmake 3.9.6 since on lower versions it could happen that cmake generates a Makefile that links against gcc_eh - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-cmake-3.9.6-1-any.pkg.tar.xz" 2> $null + # freeze the cmake to a stable version, on version < 3.9.6 cmake may generate a Makefile links against gcc_eh instead of gcc_s_eh + C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-cmake-3.11.1-2-any.pkg.tar.xz" 2> $null } before_build: diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index cb7530f2d..016c445dd 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -81,5 +81,5 @@ endif() # Cubeb if(ENABLE_CUBEB) set(BUILD_TESTS OFF CACHE BOOL "") - add_subdirectory(cubeb) + add_subdirectory(cubeb EXCLUDE_FROM_ALL) endif() diff --git a/externals/cryptopp/cryptopp b/externals/cryptopp/cryptopp index 5be140bce..f320e7d92 160000 --- a/externals/cryptopp/cryptopp +++ b/externals/cryptopp/cryptopp @@ -1 +1 @@ -Subproject commit 5be140bcea453a00f7f2fec09fb9e37849d65d98 +Subproject commit f320e7d92a33ee80ae42deef79da78cfc30868af diff --git a/externals/dynarmic b/externals/dynarmic index d1d470536..4b350a354 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit d1d4705364031512cb89333aebc00b8d75a2f732 +Subproject commit 4b350a354a21339052c7fff88832c3f81f5624be diff --git a/externals/soundtouch b/externals/soundtouch index 019d2089b..060181eaf 160000 --- a/externals/soundtouch +++ b/externals/soundtouch @@ -1 +1 @@ -Subproject commit 019d2089bbadf70d73ba85aa8ea51490b071262c +Subproject commit 060181eaf273180d3a7e87349895bd0cb6ccbf4a diff --git a/src/citra/config.cpp b/src/citra/config.cpp index b4e3a2ce9..ce51b687b 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -155,14 +155,20 @@ void Config::ReadValues() { sdl2_config->Get("Camera", "camera_outer_right_name", "blank"); Settings::values.camera_config[OuterRightCamera] = sdl2_config->Get("Camera", "camera_outer_right_config", ""); + Settings::values.camera_flip[OuterRightCamera] = + sdl2_config->GetInteger("Camera", "camera_outer_right_flip", 0); Settings::values.camera_name[InnerCamera] = sdl2_config->Get("Camera", "camera_inner_name", "blank"); Settings::values.camera_config[InnerCamera] = sdl2_config->Get("Camera", "camera_inner_config", ""); + Settings::values.camera_flip[InnerCamera] = + sdl2_config->GetInteger("Camera", "camera_inner_flip", 0); Settings::values.camera_name[OuterLeftCamera] = sdl2_config->Get("Camera", "camera_outer_left_name", "blank"); Settings::values.camera_config[OuterLeftCamera] = sdl2_config->Get("Camera", "camera_outer_left_config", ""); + Settings::values.camera_flip[OuterLeftCamera] = + sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 7179d6f94..4a17eb6ca 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -178,13 +178,19 @@ camera_outer_right_name = # A config string for the right outer camera. Its meaning is defined by the camera engine camera_outer_right_config = +# The image flip to apply +# 0: None (default), 1: Horizontal, 2: Vertical, 3: Reverse +camera_outer_right_flip = + # ... for the left outer camera camera_outer_left_name = camera_outer_left_config = +camera_outer_left_flip = # ... for the inner camera camera_inner_name = camera_inner_config = +camera_inner_flip = [Miscellaneous] # A filter which removes logs below a certain logging level. diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 4e0fcdbeb..7b0d0eaac 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -13,8 +13,8 @@ add_executable(citra-qt camera/camera_util.h camera/still_image_camera.cpp camera/still_image_camera.h - camera/qt_camera_factory.cpp - camera/qt_camera_factory.h + camera/qt_camera_base.cpp + camera/qt_camera_base.h camera/qt_multimedia_camera.cpp camera/qt_multimedia_camera.h citra-qt.rc diff --git a/src/citra_qt/camera/qt_camera_base.cpp b/src/citra_qt/camera/qt_camera_base.cpp new file mode 100644 index 000000000..69593ab51 --- /dev/null +++ b/src/citra_qt/camera/qt_camera_base.cpp @@ -0,0 +1,58 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "citra_qt/camera/camera_util.h" +#include "citra_qt/camera/qt_camera_base.h" + +namespace Camera { + +QtCameraInterface::QtCameraInterface(const Service::CAM::Flip& flip) { + using namespace Service::CAM; + flip_horizontal = basic_flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); + flip_vertical = basic_flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); +} + +void QtCameraInterface::SetFormat(Service::CAM::OutputFormat output_format) { + output_rgb = output_format == Service::CAM::OutputFormat::RGB565; +} + +void QtCameraInterface::SetResolution(const Service::CAM::Resolution& resolution) { + width = resolution.width; + height = resolution.height; +} + +void QtCameraInterface::SetFlip(Service::CAM::Flip flip) { + using namespace Service::CAM; + flip_horizontal = basic_flip_horizontal ^ (flip == Flip::Horizontal || flip == Flip::Reverse); + flip_vertical = basic_flip_vertical ^ (flip == Flip::Vertical || flip == Flip::Reverse); +} + +void QtCameraInterface::SetEffect(Service::CAM::Effect effect) { + if (effect != Service::CAM::Effect::None) { + NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast(effect)); + } +} + +std::vector QtCameraInterface::ReceiveFrame() { + return CameraUtil::ProcessImage(QtReceiveFrame(), width, height, output_rgb, flip_horizontal, + flip_vertical); +} + +std::unique_ptr QtCameraFactory::CreatePreview(const std::string& config, + int width, int height, + const Service::CAM::Flip& flip) { + std::unique_ptr camera = Create(config, flip); + + if (camera->IsPreviewAvailable()) { + return camera; + } + QMessageBox::critical( + nullptr, QObject::tr("Error"), + (config.empty() ? QObject::tr("Couldn't load the camera") + : QObject::tr("Couldn't load %1").arg(QString::fromStdString(config)))); + return nullptr; +} + +} // namespace Camera diff --git a/src/citra_qt/camera/qt_camera_base.h b/src/citra_qt/camera/qt_camera_base.h new file mode 100644 index 000000000..6c6095a28 --- /dev/null +++ b/src/citra_qt/camera/qt_camera_base.h @@ -0,0 +1,36 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "core/frontend/camera/factory.h" + +namespace Camera { + +// Base class for camera interfaces of citra_qt +class QtCameraInterface : public CameraInterface { +public: + QtCameraInterface(const Service::CAM::Flip& flip); + void SetResolution(const Service::CAM::Resolution&) override; + void SetFlip(Service::CAM::Flip) override; + void SetEffect(Service::CAM::Effect) override; + void SetFormat(Service::CAM::OutputFormat) override; + std::vector ReceiveFrame() override; + virtual QImage QtReceiveFrame() = 0; + +private: + int width, height; + bool output_rgb; + bool flip_horizontal, flip_vertical; + bool basic_flip_horizontal, basic_flip_vertical; +}; + +// Base class for camera factories of citra_qt +class QtCameraFactory : public CameraFactory { + std::unique_ptr CreatePreview(const std::string& config, int width, int height, + const Service::CAM::Flip& flip) override; +}; + +} // namespace Camera diff --git a/src/citra_qt/camera/qt_camera_factory.cpp b/src/citra_qt/camera/qt_camera_factory.cpp deleted file mode 100644 index fa78591ba..000000000 --- a/src/citra_qt/camera/qt_camera_factory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "citra_qt/camera/qt_camera_factory.h" - -namespace Camera { - -std::unique_ptr QtCameraFactory::CreatePreview(const std::string& config, - int width, int height) const { - std::unique_ptr camera = Create(config); - - if (camera->IsPreviewAvailable()) { - return camera; - } - QMessageBox::critical( - nullptr, QObject::tr("Error"), - (config.empty() ? QObject::tr("Couldn't load the camera") - : QObject::tr("Couldn't load %1").arg(QString::fromStdString(config)))); - return nullptr; -} - -} // namespace Camera diff --git a/src/citra_qt/camera/qt_camera_factory.h b/src/citra_qt/camera/qt_camera_factory.h deleted file mode 100644 index 7efc21b58..000000000 --- a/src/citra_qt/camera/qt_camera_factory.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include "core/frontend/camera/factory.h" - -namespace Camera { - -// Base class for camera factories of citra_qt -class QtCameraFactory : public CameraFactory { - std::unique_ptr CreatePreview(const std::string& config, int width, - int height) const override; -}; - -} // namespace Camera diff --git a/src/citra_qt/camera/qt_multimedia_camera.cpp b/src/citra_qt/camera/qt_multimedia_camera.cpp index 6c2668df6..76a21453d 100644 --- a/src/citra_qt/camera/qt_multimedia_camera.cpp +++ b/src/citra_qt/camera/qt_multimedia_camera.cpp @@ -46,8 +46,9 @@ bool QtCameraSurface::present(const QVideoFrame& frame) { return true; } -QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name) - : handler(QtMultimediaCameraHandler::GetHandler()) { +QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name, + const Service::CAM::Flip& flip) + : QtCameraInterface(flip), handler(QtMultimediaCameraHandler::GetHandler(camera_name)) { if (handler->thread() == QThread::currentThread()) { handler->CreateCamera(camera_name); } else { @@ -73,10 +74,6 @@ void QtMultimediaCamera::StopCapture() { handler->StopCamera(); } -void QtMultimediaCamera::SetFormat(Service::CAM::OutputFormat output_format) { - output_rgb = output_format == Service::CAM::OutputFormat::RGB565; -} - void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { const std::array FrameRateList = { /* Rate_15 */ QCamera::FrameRateRange(15, 15), @@ -96,57 +93,49 @@ void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { auto framerate = FrameRateList[static_cast(frame_rate)]; - handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); - handler->settings.setMinimumFrameRate(framerate.maximumFrameRate); -} - -void QtMultimediaCamera::SetResolution(const Service::CAM::Resolution& resolution) { - width = resolution.width; - height = resolution.height; -} - -void QtMultimediaCamera::SetFlip(Service::CAM::Flip flip) { - using namespace Service::CAM; - flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); - flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); -} - -void QtMultimediaCamera::SetEffect(Service::CAM::Effect effect) { - if (effect != Service::CAM::Effect::None) { - NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast(effect)); + if (handler->camera->supportedViewfinderFrameRateRanges().contains(framerate)) { + handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); + handler->settings.setMaximumFrameRate(framerate.maximumFrameRate); } } -std::vector QtMultimediaCamera::ReceiveFrame() { +QImage QtMultimediaCamera::QtReceiveFrame() { QMutexLocker locker(&handler->camera_surface.mutex); - return CameraUtil::ProcessImage(handler->camera_surface.current_frame, width, height, - output_rgb, flip_horizontal, flip_vertical); + return handler->camera_surface.current_frame; } bool QtMultimediaCamera::IsPreviewAvailable() { return handler->CameraAvailable(); } -std::unique_ptr QtMultimediaCameraFactory::Create( - const std::string& config) const { - return std::make_unique(config); +std::unique_ptr QtMultimediaCameraFactory::Create(const std::string& config, + const Service::CAM::Flip& flip) { + return std::make_unique(config, flip); } std::array, 3> QtMultimediaCameraHandler::handlers; std::array QtMultimediaCameraHandler::status; +std::unordered_map> + QtMultimediaCameraHandler::loaded; + void QtMultimediaCameraHandler::Init() { for (auto& handler : handlers) { handler = std::make_shared(); } } -std::shared_ptr QtMultimediaCameraHandler::GetHandler() { +std::shared_ptr QtMultimediaCameraHandler::GetHandler( + const std::string& camera_name) { + if (loaded.count(camera_name)) { + return loaded.at(camera_name); + } for (int i = 0; i < handlers.size(); i++) { if (!status[i]) { NGLOG_INFO(Service_CAM, "Successfully got handler {}", i); status[i] = true; + loaded.emplace(camera_name, handlers[i]); return handlers[i]; } } @@ -161,6 +150,12 @@ void QtMultimediaCameraHandler::ReleaseHandler( NGLOG_INFO(Service_CAM, "Successfully released handler {}", i); status[i] = false; handlers[i]->started = false; + for (auto it = loaded.begin(); it != loaded.end(); it++) { + if (it->second == handlers[i]) { + loaded.erase(it); + break; + } + } break; } } @@ -178,6 +173,7 @@ void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) { settings.setMinimumFrameRate(30); settings.setMaximumFrameRate(30); camera->setViewfinder(&camera_surface); + camera->load(); } void QtMultimediaCameraHandler::StopCamera() { diff --git a/src/citra_qt/camera/qt_multimedia_camera.h b/src/citra_qt/camera/qt_multimedia_camera.h index 103aff8a0..14242c554 100644 --- a/src/citra_qt/camera/qt_multimedia_camera.h +++ b/src/citra_qt/camera/qt_multimedia_camera.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -13,7 +14,7 @@ #include #include #include "citra_qt/camera/camera_util.h" -#include "citra_qt/camera/qt_camera_factory.h" +#include "citra_qt/camera/qt_camera_base.h" #include "core/frontend/camera/interface.h" class GMainWindow; @@ -36,30 +37,24 @@ private: class QtMultimediaCameraHandler; /// This class is only an interface. It just calls QtMultimediaCameraHandler. -class QtMultimediaCamera final : public CameraInterface { +class QtMultimediaCamera final : public QtCameraInterface { public: - QtMultimediaCamera(const std::string& camera_name); + QtMultimediaCamera(const std::string& camera_name, const Service::CAM::Flip& flip); ~QtMultimediaCamera(); void StartCapture() override; void StopCapture() override; - void SetResolution(const Service::CAM::Resolution&) override; - void SetFlip(Service::CAM::Flip) override; - void SetEffect(Service::CAM::Effect) override; - void SetFormat(Service::CAM::OutputFormat) override; void SetFrameRate(Service::CAM::FrameRate frame_rate) override; - std::vector ReceiveFrame() override; + QImage QtReceiveFrame() override; bool IsPreviewAvailable() override; private: std::shared_ptr handler; - int width, height; - bool output_rgb; - bool flip_horizontal, flip_vertical; }; class QtMultimediaCameraFactory final : public QtCameraFactory { public: - std::unique_ptr Create(const std::string& config) const override; + std::unique_ptr Create(const std::string& config, + const Service::CAM::Flip& flip) override; }; class QtMultimediaCameraHandler final : public QObject { @@ -68,7 +63,7 @@ class QtMultimediaCameraHandler final : public QObject { public: /// Creates the global handler. Must be called in UI thread. static void Init(); - static std::shared_ptr GetHandler(); + static std::shared_ptr GetHandler(const std::string& camera_name); static void ReleaseHandler(const std::shared_ptr& handler); /** @@ -98,6 +93,7 @@ private: static std::array, 3> handlers; static std::array status; + static std::unordered_map> loaded; friend class QtMultimediaCamera; // For access to camera_surface (and camera) }; diff --git a/src/citra_qt/camera/still_image_camera.cpp b/src/citra_qt/camera/still_image_camera.cpp index ab0d18308..e02d0d0d3 100644 --- a/src/citra_qt/camera/still_image_camera.cpp +++ b/src/citra_qt/camera/still_image_camera.cpp @@ -5,47 +5,36 @@ #include #include #include +#include #include "citra_qt/camera/still_image_camera.h" namespace Camera { -StillImageCamera::StillImageCamera(QImage image_) : image(std::move(image_)) {} +StillImageCamera::StillImageCamera(QImage image_, const Service::CAM::Flip& flip) + : QtCameraInterface(flip), image(std::move(image_)) {} + +StillImageCamera::~StillImageCamera() { + StillImageCameraFactory::last_path.clear(); +} void StillImageCamera::StartCapture() {} void StillImageCamera::StopCapture() {} -void StillImageCamera::SetFormat(Service::CAM::OutputFormat output_format) { - output_rgb = output_format == Service::CAM::OutputFormat::RGB565; -} - -void StillImageCamera::SetResolution(const Service::CAM::Resolution& resolution) { - width = resolution.width; - height = resolution.height; -} - -void StillImageCamera::SetFlip(Service::CAM::Flip flip) { - using namespace Service::CAM; - flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); - flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); -} - -void StillImageCamera::SetEffect(Service::CAM::Effect effect) { - if (effect != Service::CAM::Effect::None) { - NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast(effect)); - } -} - -std::vector StillImageCamera::ReceiveFrame() { - return CameraUtil::ProcessImage(image, width, height, output_rgb, flip_horizontal, - flip_vertical); +QImage StillImageCamera::QtReceiveFrame() { + return image; } bool StillImageCamera::IsPreviewAvailable() { return !image.isNull(); } -const std::string StillImageCameraFactory::getFilePath() { +std::string StillImageCameraFactory::last_path; + +const std::string StillImageCameraFactory::GetFilePath() const { + if (!last_path.empty()) { + return last_path; + } QList types = QImageReader::supportedImageFormats(); QList temp_filters; for (QByteArray type : types) { @@ -53,21 +42,29 @@ const std::string StillImageCameraFactory::getFilePath() { } QString filter = QObject::tr("Supported image files (%1)").arg(temp_filters.join(" ")); - - return QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), ".", filter) - .toStdString(); + last_path = + QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), ".", filter).toStdString(); + return last_path; } -std::unique_ptr StillImageCameraFactory::Create(const std::string& config) const { +std::unique_ptr StillImageCameraFactory::Create(const std::string& config, + const Service::CAM::Flip& flip) { std::string real_config = config; if (config.empty()) { - real_config = getFilePath(); + // call GetFilePath() in UI thread (note: StillImageCameraFactory itself is initialized in + // UI thread, so we can just pass in "this" here) + if (thread() == QThread::currentThread()) { + real_config = GetFilePath(); + } else { + QMetaObject::invokeMethod(this, "GetFilePath", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(std::string, real_config)); + } } QImage image(QString::fromStdString(real_config)); if (image.isNull()) { NGLOG_ERROR(Service_CAM, "Couldn't load image \"{}\"", real_config.c_str()); } - return std::make_unique(image); + return std::make_unique(image, flip); } } // namespace Camera diff --git a/src/citra_qt/camera/still_image_camera.h b/src/citra_qt/camera/still_image_camera.h index c54d367e8..19ca044f0 100644 --- a/src/citra_qt/camera/still_image_camera.h +++ b/src/citra_qt/camera/still_image_camera.h @@ -7,37 +7,39 @@ #include #include #include "citra_qt/camera/camera_util.h" -#include "citra_qt/camera/qt_camera_factory.h" +#include "citra_qt/camera/qt_camera_base.h" #include "core/frontend/camera/interface.h" namespace Camera { -class StillImageCamera final : public CameraInterface { +class StillImageCamera final : public QtCameraInterface { public: - StillImageCamera(QImage image); + StillImageCamera(QImage image, const Service::CAM::Flip& flip); + ~StillImageCamera(); void StartCapture() override; void StopCapture() override; - void SetResolution(const Service::CAM::Resolution&) override; - void SetFlip(Service::CAM::Flip) override; - void SetEffect(Service::CAM::Effect) override; - void SetFormat(Service::CAM::OutputFormat) override; void SetFrameRate(Service::CAM::FrameRate frame_rate) override {} - std::vector ReceiveFrame() override; + QImage QtReceiveFrame() override; bool IsPreviewAvailable() override; private: QImage image; - int width, height; - bool output_rgb; - bool flip_horizontal, flip_vertical; }; -class StillImageCameraFactory final : public QtCameraFactory { +class StillImageCameraFactory final : public QObject, public QtCameraFactory { + Q_OBJECT + public: - std::unique_ptr Create(const std::string& config) const override; + std::unique_ptr Create(const std::string& config, + const Service::CAM::Flip& flip) override; + + Q_INVOKABLE const std::string GetFilePath() const; private: - static const std::string getFilePath(); + /// Record the path chosen to avoid multiple prompt problem + static std::string last_path; + + friend class StillImageCamera; }; } // namespace Camera diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index bdb296659..e5d252c81 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -128,14 +128,19 @@ void Config::ReadValues() { qt_config->value("camera_outer_right_name", "blank").toString().toStdString(); Settings::values.camera_config[OuterRightCamera] = qt_config->value("camera_outer_right_config", "").toString().toStdString(); + Settings::values.camera_flip[OuterRightCamera] = + qt_config->value("camera_outer_right_flip", "0").toInt(); Settings::values.camera_name[InnerCamera] = qt_config->value("camera_inner_name", "blank").toString().toStdString(); Settings::values.camera_config[InnerCamera] = qt_config->value("camera_inner_config", "").toString().toStdString(); + Settings::values.camera_flip[InnerCamera] = qt_config->value("camera_inner_flip", "").toInt(); Settings::values.camera_name[OuterLeftCamera] = qt_config->value("camera_outer_left_name", "blank").toString().toStdString(); Settings::values.camera_config[OuterLeftCamera] = qt_config->value("camera_outer_left_config", "").toString().toStdString(); + Settings::values.camera_flip[OuterLeftCamera] = + qt_config->value("camera_outer_left_flip", "").toInt(); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); @@ -317,14 +322,17 @@ void Config::SaveValues() { QString::fromStdString(Settings::values.camera_name[OuterRightCamera])); qt_config->setValue("camera_outer_right_config", QString::fromStdString(Settings::values.camera_config[OuterRightCamera])); + qt_config->setValue("camera_outer_right_flip", Settings::values.camera_flip[OuterRightCamera]); qt_config->setValue("camera_inner_name", QString::fromStdString(Settings::values.camera_name[InnerCamera])); qt_config->setValue("camera_inner_config", QString::fromStdString(Settings::values.camera_config[InnerCamera])); + qt_config->setValue("camera_inner_flip", Settings::values.camera_flip[InnerCamera]); qt_config->setValue("camera_outer_left_name", QString::fromStdString(Settings::values.camera_name[OuterLeftCamera])); qt_config->setValue("camera_outer_left_config", QString::fromStdString(Settings::values.camera_config[OuterLeftCamera])); + qt_config->setValue("camera_outer_left_flip", Settings::values.camera_flip[OuterLeftCamera]); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); diff --git a/src/citra_qt/configuration/configure_camera.cpp b/src/citra_qt/configuration/configure_camera.cpp index ca6db4623..e9d964b3a 100644 --- a/src/citra_qt/configuration/configure_camera.cpp +++ b/src/citra_qt/configuration/configure_camera.cpp @@ -27,14 +27,7 @@ ConfigureCamera::ConfigureCamera(QWidget* parent) // Load settings camera_name = Settings::values.camera_name; camera_config = Settings::values.camera_config; - for (auto&& item : camera_name) { - if (item == "opencv") { - QMessageBox::critical(this, tr("Error"), - tr("Sorry, Citra has removed support for OpenCV cameras.\n\nYour " - "existing OpenCV cameras have been replaced with Blank.")); - item = "blank"; - } - } + camera_flip = Settings::values.camera_flip; QList cameras = QCameraInfo::availableCameras(); for (const QCameraInfo& cameraInfo : cameras) { ui->system_camera->addItem(cameraInfo.deviceName()); @@ -98,6 +91,8 @@ void ConfigureCamera::connectEvents() { connect(ui->system_camera, static_cast(&QComboBox::currentIndexChanged), this, [=] { stopPreviewing(); }); + connect(ui->camera_flip, static_cast(&QComboBox::currentIndexChanged), + this, [=] { stopPreviewing(); }); } void ConfigureCamera::updateCameraMode() { @@ -148,6 +143,8 @@ void ConfigureCamera::updateImageSourceUI() { } ui->system_camera_label->setHidden(image_source != 2); ui->system_camera->setHidden(image_source != 2); + ui->camera_flip_label->setHidden(image_source == 0); + ui->camera_flip->setHidden(image_source == 0); } void ConfigureCamera::recordConfig() { @@ -166,10 +163,12 @@ void ConfigureCamera::recordConfig() { if (current_selected == CameraPosition::RearBoth) { camera_name[0] = camera_name[2] = implementation; camera_config[0] = camera_config[2] = config; + camera_flip[0] = camera_flip[2] = ui->camera_flip->currentIndex(); } else if (current_selected != CameraPosition::Null) { int index = static_cast(current_selected); camera_name[index] = implementation; camera_config[index] = config; + camera_flip[index] = ui->camera_flip->currentIndex(); } current_selected = getCameraSelection(); } @@ -187,9 +186,9 @@ void ConfigureCamera::startPreviewing() { ui->preview_box->setToolTip(tr("Resolution: ") + QString::number(preview_width) + "*" + QString::number(preview_height)); // Load previewing camera - previewing_camera = - Camera::CreateCameraPreview(camera_name[camera_selection], camera_config[camera_selection], - preview_width, preview_height); + previewing_camera = Camera::CreateCameraPreview( + camera_name[camera_selection], camera_config[camera_selection], preview_width, + preview_height, static_cast(camera_flip[camera_selection])); if (!previewing_camera) { stopPreviewing(); return; @@ -262,6 +261,7 @@ void ConfigureCamera::setConfiguration() { } else { ui->camera_file->setText(QString::fromStdString(camera_config[index])); } + ui->camera_flip->setCurrentIndex(camera_flip[index]); updateImageSourceUI(); } @@ -288,6 +288,7 @@ void ConfigureCamera::applyConfiguration() { stopPreviewing(); Settings::values.camera_name = camera_name; Settings::values.camera_config = camera_config; + Settings::values.camera_flip = camera_flip; Settings::Apply(); } diff --git a/src/citra_qt/configuration/configure_camera.h b/src/citra_qt/configuration/configure_camera.h index 5621de7ef..0aa55996e 100644 --- a/src/citra_qt/configuration/configure_camera.h +++ b/src/citra_qt/configuration/configure_camera.h @@ -47,6 +47,7 @@ private: std::unique_ptr ui; std::array camera_name; std::array camera_config; + std::array camera_flip; int timer_id = 0; int preview_width = 0; int preview_height = 0; diff --git a/src/citra_qt/configuration/configure_camera.ui b/src/citra_qt/configuration/configure_camera.ui index cd4d2e108..46c56b266 100644 --- a/src/citra_qt/configuration/configure_camera.ui +++ b/src/citra_qt/configuration/configure_camera.ui @@ -1,257 +1,347 @@ - - ConfigureCamera - - + ConfigureCamera + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Camera + + - - - Camera + + + + + Select the camera to configure - - - - - - - Select the camera to configure - - - Camera to configure: - - - - - - - Select the camera to configure - - - - Front - - - - - Rear - - - - - - - - - - - - Select the camera mode (single or double) - - - Camera mode: - - - - - - - Select the camera mode (single or double) - - - - Single (2D) - - - - - Double (3D) - - - - - - - - - - - - Select the position of camera to configure - - - Camera position: - - - - - - - Select the position of camera to configure - - - - Left - - - - - Right - - - - - - - - + + Camera to configure: + + + + + + + Select the camera to configure + + + + Front + + + + + Rear + + + + + - - - Configuration + + + + + Select the camera mode (single or double) - - - - - - - Select where the image of the emulated camera comes from. It may be an image or a real camera. - - - Camera Image Source: - - - - - - - Select where the image of the emulated camera come from. It may be an image or a real camera. - - - - Blank (blank) - - - - - Still Image (image) - - - - - System Camera (qt) - - - - - - - - - - - - QFrame::NoFrame - - - File: - - - - - - - - - - ... - - - - - - - - - - - QFrame::NoFrame - - - Camera: - - - - - - - - <Default> - - - - - - - - - - Prompt before load - - - - - + + Camera mode: + + + + + + + Select the camera mode (single or double) + + + + Single (2D) + + + + + Double (3D) + + + + + - - - Preview + + + + + Select the position of camera to configure - - - - - - 512 - 384 - - - - Resolution: 512*384 - - - - - - - - - - Click to preview - - - - - + + Camera position: + + + + + + + Select the position of camera to configure + + + + Left + + + + + Right + + + + + + + + + + + + + Configuration + + + + + + + + Select where the image of the emulated camera comes from. It may be an image or a real camera. + + + Camera Image Source: + + + + + + + Select where the image of the emulated camera comes from. It may be an image or a real camera. + + + + Blank (blank) + + + + + Still Image (image) + + + + + System Camera (qt) + + + + + - - - Qt::Vertical + + + + + QFrame::NoFrame - - - 20 - 40 - + + File: - + + + + + + + + + ... + + + + - - - - + + + + + + QFrame::NoFrame + + + Select the system camera to use + + + Camera: + + + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + Select the system camera to use + + + + <Default> + + + + + + + + + + + + QFrame::NoFrame + + + Select the image flip to apply + + + Flip: + + + + + + + true + + + + 0 + 0 + + + + + 800 + 16777215 + + + + Select the image flip to apply + + + + None + + + + + Horizontal + + + + + Vertical + + + + + Reverse + + + + + + + + + + Select an image file every time before the camera is loaded + + + Prompt before load + + + + + + + + + + Preview + + + + + + + 512 + 384 + + + + Resolution: 512*384 + + + + + + + + + + Click to preview + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 1546709aa..a9341de00 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -173,14 +173,20 @@ public: &extension); QString title = data(TitleRole).toString(); QString second_name = QString::fromStdString(filename + extension); - static QRegExp installed_system_pattern( + static QRegExp installed_pattern( QString::fromStdString( FileUtil::GetUserPath(D_SDMC_IDX) + "Nintendo " "3DS/00000000000000000000000000000000/00000000000000000000000000000000/" - "title/000400(0|1)0/[0-9a-f]{8}/content/") + "title/0004000(0|e)/[0-9a-f]{8}/content/") .replace("\\", "\\\\")); - if (installed_system_pattern.exactMatch(QString::fromStdString(path))) { + static QRegExp system_pattern( + QString::fromStdString(FileUtil::GetUserPath(D_NAND_IDX) + + "00000000000000000000000000000000/" + "title/00040010/[0-9a-f]{8}/content/") + .replace("\\", "\\\\")); + if (installed_pattern.exactMatch(QString::fromStdString(path)) || + system_pattern.exactMatch(QString::fromStdString(path))) { // Use a different mechanism for system / installed titles showing program ID second_name = QString("%1-%2") .arg(data(ProgramIdRole).toULongLong(), 16, 16, QChar('0')) diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index b8c0cb34e..9548d0e16 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -3866,6 +3866,8 @@ SWI_INST : { num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs = 0; Kernel::CallSVC(inst_cream->num & 0xFFFF); + // The kernel would call ERET to get here, which clears exclusive memory state. + cpu->UnsetExclusiveMemoryAddress(); } cpu->Reg[15] += cpu->GetInstructionSize(); diff --git a/src/core/frontend/camera/factory.cpp b/src/core/frontend/camera/factory.cpp index 4abbd9ff7..619fa3974 100644 --- a/src/core/frontend/camera/factory.cpp +++ b/src/core/frontend/camera/factory.cpp @@ -17,28 +17,29 @@ void RegisterFactory(const std::string& name, std::unique_ptr fac factories[name] = std::move(factory); } -std::unique_ptr CreateCamera(const std::string& name, const std::string& config) { +std::unique_ptr CreateCamera(const std::string& name, const std::string& config, + const Service::CAM::Flip& flip) { auto pair = factories.find(name); if (pair != factories.end()) { - return pair->second->Create(config); + return pair->second->Create(config, flip); } if (name != "blank") { - NGLOG_ERROR(Service_CAM, "Unknown camera \"{}\"", name); + NGLOG_ERROR(Service_CAM, "Unknown camera {}", name); } return std::make_unique(); } std::unique_ptr CreateCameraPreview(const std::string& name, const std::string& config, int width, - int height) { + int height, const Service::CAM::Flip& flip) { auto pair = factories.find(name); if (pair != factories.end()) { - return pair->second->CreatePreview(config, width, height); + return pair->second->CreatePreview(config, width, height, flip); } if (name != "blank") { - NGLOG_ERROR(Service_CAM, "Unknown camera \"{}\"", name); + NGLOG_ERROR(Service_CAM, "Unknown camera {}", name); } return std::make_unique(); } diff --git a/src/core/frontend/camera/factory.h b/src/core/frontend/camera/factory.h index cf09b7bb0..0fe9f314f 100644 --- a/src/core/frontend/camera/factory.h +++ b/src/core/frontend/camera/factory.h @@ -18,22 +18,26 @@ public: * Creates a camera object based on the configuration string. * @param config Configuration string to create the camera. The implementation can decide the * meaning of this string. + * @param flip The image flip to apply * @returns a unique_ptr to the created camera object. */ - virtual std::unique_ptr Create(const std::string& config) const = 0; + virtual std::unique_ptr Create(const std::string& config, + const Service::CAM::Flip& flip) = 0; /** * Creates a camera object for preview based on the configuration string. * @param config Configuration string to create the camera. The implementation can decide the * meaning of this string. + * @param flip The image flip to apply * @returns a unique_ptr to the created camera object. * Note: The default implementation for this is to call Create(). Derived classes may have other * Implementations. For example, A dialog may be used instead of NGLOG_ERROR when error * occurs. */ virtual std::unique_ptr CreatePreview(const std::string& config, int width, - int height) const { - return Create(config); + int height, + const Service::CAM::Flip& flip) { + return Create(config, flip); } }; @@ -50,7 +54,8 @@ void RegisterFactory(const std::string& name, std::unique_ptr fac * @param config Configuration string to create the camera. The meaning of this string is * defined by the factory. */ -std::unique_ptr CreateCamera(const std::string& name, const std::string& config); +std::unique_ptr CreateCamera(const std::string& name, const std::string& config, + const Service::CAM::Flip& flip); /** * Creates a camera from the factory for previewing. @@ -60,6 +65,6 @@ std::unique_ptr CreateCamera(const std::string& name, const std */ std::unique_ptr CreateCameraPreview(const std::string& name, const std::string& config, int width, - int height); + int height, const Service::CAM::Flip& flip); } // namespace Camera diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index 7eae94972..974b5ed29 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -404,7 +404,7 @@ void ReceiveProperty(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x16, 0x2, 0x2); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; // stub 0 (32 bit value) + cmd_buff[2] = buff_size; // Should be actual number of read bytes. cmd_buff[3] = (buff_size << 4 | 0xC); cmd_buff[4] = buff_addr; diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 3328dcf8e..78cdb23e8 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -1041,8 +1041,9 @@ void Module::ReloadCameraDevices() { } void Module::LoadCameraImplementation(CameraConfig& camera, int camera_id) { - camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id], - Settings::values.camera_config[camera_id]); + camera.impl = Camera::CreateCamera( + Settings::values.camera_name[camera_id], Settings::values.camera_config[camera_id], + static_cast(Settings::values.camera_flip[camera_id])); camera.impl->SetFlip(camera.contexts[0].flip); camera.impl->SetEffect(camera.contexts[0].effect); camera.impl->SetFormat(camera.contexts[0].format); diff --git a/src/core/settings.h b/src/core/settings.h index b57a1cbed..5ce4811af 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -140,6 +140,7 @@ struct Values { // Camera std::array camera_name; std::array camera_config; + std::array camera_flip; // Debugging bool use_gdbstub; diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp index 06c6f52e5..b885b5211 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.cpp +++ b/src/video_core/shader/shader_jit_x64_compiler.cpp @@ -33,70 +33,70 @@ namespace Shader { typedef void (JitShader::*JitFunction)(Instruction instr); const JitFunction instr_table[64] = { - &JitShader::Compile_ADD, // add - &JitShader::Compile_DP3, // dp3 - &JitShader::Compile_DP4, // dp4 - &JitShader::Compile_DPH, // dph - nullptr, // unknown - &JitShader::Compile_EX2, // ex2 - &JitShader::Compile_LG2, // lg2 - nullptr, // unknown - &JitShader::Compile_MUL, // mul - &JitShader::Compile_SGE, // sge - &JitShader::Compile_SLT, // slt - &JitShader::Compile_FLR, // flr - &JitShader::Compile_MAX, // max - &JitShader::Compile_MIN, // min - &JitShader::Compile_RCP, // rcp - &JitShader::Compile_RSQ, // rsq - nullptr, // unknown - nullptr, // unknown - &JitShader::Compile_MOVA, // mova - &JitShader::Compile_MOV, // mov - nullptr, // unknown - nullptr, // unknown - nullptr, // unknown - nullptr, // unknown - &JitShader::Compile_DPH, // dphi - nullptr, // unknown - &JitShader::Compile_SGE, // sgei - &JitShader::Compile_SLT, // slti - nullptr, // unknown - nullptr, // unknown - nullptr, // unknown - nullptr, // unknown - nullptr, // unknown - &JitShader::Compile_NOP, // nop - &JitShader::Compile_END, // end - nullptr, // break - &JitShader::Compile_CALL, // call - &JitShader::Compile_CALLC, // callc - &JitShader::Compile_CALLU, // callu - &JitShader::Compile_IF, // ifu - &JitShader::Compile_IF, // ifc - &JitShader::Compile_LOOP, // loop - &JitShader::Compile_EMIT, // emit - &JitShader::Compile_SETE, // sete - &JitShader::Compile_JMP, // jmpc - &JitShader::Compile_JMP, // jmpu - &JitShader::Compile_CMP, // cmp - &JitShader::Compile_CMP, // cmp - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // madi - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad - &JitShader::Compile_MAD, // mad + &JitShader::Compile_ADD, // add + &JitShader::Compile_DP3, // dp3 + &JitShader::Compile_DP4, // dp4 + &JitShader::Compile_DPH, // dph + nullptr, // unknown + &JitShader::Compile_EX2, // ex2 + &JitShader::Compile_LG2, // lg2 + nullptr, // unknown + &JitShader::Compile_MUL, // mul + &JitShader::Compile_SGE, // sge + &JitShader::Compile_SLT, // slt + &JitShader::Compile_FLR, // flr + &JitShader::Compile_MAX, // max + &JitShader::Compile_MIN, // min + &JitShader::Compile_RCP, // rcp + &JitShader::Compile_RSQ, // rsq + nullptr, // unknown + nullptr, // unknown + &JitShader::Compile_MOVA, // mova + &JitShader::Compile_MOV, // mov + nullptr, // unknown + nullptr, // unknown + nullptr, // unknown + nullptr, // unknown + &JitShader::Compile_DPH, // dphi + nullptr, // unknown + &JitShader::Compile_SGE, // sgei + &JitShader::Compile_SLT, // slti + nullptr, // unknown + nullptr, // unknown + nullptr, // unknown + nullptr, // unknown + nullptr, // unknown + &JitShader::Compile_NOP, // nop + &JitShader::Compile_END, // end + &JitShader::Compile_BREAKC, // breakc + &JitShader::Compile_CALL, // call + &JitShader::Compile_CALLC, // callc + &JitShader::Compile_CALLU, // callu + &JitShader::Compile_IF, // ifu + &JitShader::Compile_IF, // ifc + &JitShader::Compile_LOOP, // loop + &JitShader::Compile_EMIT, // emit + &JitShader::Compile_SETE, // sete + &JitShader::Compile_JMP, // jmpc + &JitShader::Compile_JMP, // jmpu + &JitShader::Compile_CMP, // cmp + &JitShader::Compile_CMP, // cmp + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // madi + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad + &JitShader::Compile_MAD, // mad }; // The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can @@ -580,10 +580,30 @@ void JitShader::Compile_RSQ(Instruction instr) { void JitShader::Compile_NOP(Instruction instr) {} void JitShader::Compile_END(Instruction instr) { + // Save conditional code + mov(byte[STATE + offsetof(UnitState, conditional_code[0])], COND0.cvt8()); + mov(byte[STATE + offsetof(UnitState, conditional_code[1])], COND1.cvt8()); + + // Save address/loop registers + sar(ADDROFFS_REG_0, 4); + sar(ADDROFFS_REG_1, 4); + sar(LOOPCOUNT_REG, 4); + mov(dword[STATE + offsetof(UnitState, address_registers[0])], ADDROFFS_REG_0.cvt32()); + mov(dword[STATE + offsetof(UnitState, address_registers[1])], ADDROFFS_REG_1.cvt32()); + mov(dword[STATE + offsetof(UnitState, address_registers[2])], LOOPCOUNT_REG); + ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8, 16); ret(); } +void JitShader::Compile_BREAKC(Instruction instr) { + Compile_Assert(looping, "BREAKC must be inside a LOOP"); + if (looping) { + Compile_EvaluateCondition(instr); + jnz(*loop_break_label); + } +} + void JitShader::Compile_CALL(Instruction instr) { // Push offset of the return push(qword, (instr.flow_control.dest_offset + instr.flow_control.num_instructions)); @@ -727,11 +747,14 @@ void JitShader::Compile_LOOP(Instruction instr) { Label l_loop_start; L(l_loop_start); + loop_break_label = Xbyak::Label(); Compile_Block(instr.flow_control.dest_offset + 1); add(LOOPCOUNT_REG, LOOPINC); // Increment LOOPCOUNT_REG by Z-component sub(LOOPCOUNT, 1); // Increment loop count by 1 jnz(l_loop_start); // Loop if not equal + L(*loop_break_label); + loop_break_label = boost::none; looping = false; } @@ -885,10 +908,17 @@ void JitShader::Compile(const std::array* program_ mov(UNIFORMS, ABI_PARAM1); mov(STATE, ABI_PARAM2); - // Zero address/loop registers - xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); - xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); - xor_(LOOPCOUNT_REG, LOOPCOUNT_REG); + // Load address/loop registers + movsxd(ADDROFFS_REG_0, dword[STATE + offsetof(UnitState, address_registers[0])]); + movsxd(ADDROFFS_REG_1, dword[STATE + offsetof(UnitState, address_registers[1])]); + mov(LOOPCOUNT_REG, dword[STATE + offsetof(UnitState, address_registers[2])]); + shl(ADDROFFS_REG_0, 4); + shl(ADDROFFS_REG_1, 4); + shl(LOOPCOUNT_REG, 4); + + // Load conditional code + mov(COND0, byte[STATE + offsetof(UnitState, conditional_code[0])]); + mov(COND1, byte[STATE + offsetof(UnitState, conditional_code[1])]); // Used to set a register to one static const __m128 one = {1.f, 1.f, 1.f, 1.f}; diff --git a/src/video_core/shader/shader_jit_x64_compiler.h b/src/video_core/shader/shader_jit_x64_compiler.h index 923c34f0d..8e1c87346 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.h +++ b/src/video_core/shader/shader_jit_x64_compiler.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "common/bit_set.h" @@ -58,6 +59,7 @@ public: void Compile_MOV(Instruction instr); void Compile_NOP(Instruction instr); void Compile_END(Instruction instr); + void Compile_BREAKC(Instruction instr); void Compile_CALL(Instruction instr); void Compile_CALLC(Instruction instr); void Compile_CALLU(Instruction instr); @@ -119,6 +121,10 @@ private: /// Mapping of Pica VS instructions to pointers in the emitted code std::array instruction_labels; + /// Label pointing to the end of the current LOOP block. Used by the BREAKC instruction to break + /// out of the loop. + boost::optional loop_break_label; + /// Offsets in code where a return needs to be inserted std::vector return_offsets;