Merge pull request #2086 from linkmauve/clang-format

Add clang-format as part of our {commit,travis}-time checks
This commit is contained in:
Yuri Kunde Schlesner 2016-09-21 11:29:48 -07:00 committed by GitHub
commit d5d2ca8058
401 changed files with 19654 additions and 18552 deletions

View file

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -e set -e
set -x set -x
@ -9,6 +9,39 @@ if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
exit 1 exit 1
fi fi
# Only run clang-format on Linux because we don't have 4.0 on OS X images
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-4.0
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
# Get list of every file modified in this pull request
files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$')"
else
# Check everything for branch pushes
files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
fi
# Turn off tracing for this because it's too verbose
set +x
for f in $files_to_lint; do
d=$(diff -u "$f" <($CLANG_FORMAT "$f"))
if ! [ -z "$d" ]; then
echo "!!! $f not compliant to coding style, here is the fix:"
echo "$d"
fail=1
fi
done
set -x
if [ "$fail" = 1 ]; then
exit 1
fi
fi
#if OS is linux or is not set #if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CC=gcc-6 export CC=gcc-6

View file

@ -17,6 +17,7 @@ addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-precise
packages: packages:
- gcc-6 - gcc-6
- g++-6 - g++-6
@ -25,6 +26,7 @@ addons:
- xorg-dev - xorg-dev
- lib32stdc++6 # For CMake - lib32stdc++6 # For CMake
- lftp # To upload builds - lftp # To upload builds
- clang-format-4.0
cache: cache:
directories: directories:

View file

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# Enforce citra's whitespace policy # Enforce citra's whitespace policy
git config --local core.whitespace tab-in-indent,trailing-space git config --local core.whitespace tab-in-indent,trailing-space
@ -7,7 +7,7 @@ paths_to_check="src/ CMakeLists.txt"
# If there are whitespace errors, print the offending file names and fail. # If there are whitespace errors, print the offending file names and fail.
if ! git diff --cached --check -- $paths_to_check ; then if ! git diff --cached --check -- $paths_to_check ; then
cat<<END; cat<<END
Error: This commit would contain trailing spaces or tabs, which is against this repo's policy. Error: This commit would contain trailing spaces or tabs, which is against this repo's policy.
Please correct those issues before commiting. (Use 'git diff --check' for more details) Please correct those issues before commiting. (Use 'git diff --check' for more details)
@ -18,9 +18,26 @@ fi
# Check for tabs, since tab-in-indent catches only those at the beginning of a line # Check for tabs, since tab-in-indent catches only those at the beginning of a line
if git diff --cached -- $paths_to_check | egrep '^\+.* '; then if git diff --cached -- $paths_to_check | egrep '^\+.* '; then
cat<<END; cat<<END
Error: This commit would contain a tab, which is against this repo's policy. Error: This commit would contain a tab, which is against this repo's policy.
If you know what you are doing, you can try 'git commit --no-verify' to bypass the check. If you know what you are doing, you can try 'git commit --no-verify' to bypass the check.
END END
exit 1 exit 1
fi fi
for f in $(git diff --name-only --diff-filter=ACMRTUXB --cached); do
if ! echo "$f" | egrep -q "[.](cpp|h)$"; then
continue
fi
if ! echo "$f" | egrep -q "^src/"; then
continue
fi
d=$(diff -u "$f" <(clang-format "$f"))
if ! [ -z "$d" ]; then
echo "!!! $f not compliant to coding style, here is the fix:"
echo "$d"
fail=1
fi
done
exit "$fail"

88
src/.clang-format Normal file
View file

@ -0,0 +1,88 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^\<[^Q][^/.>]*\>'
Priority: -2
- Regex: '^\<'
Priority: -1
- Regex: '^\"'
Priority: 0
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 150
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

View file

@ -4,14 +4,12 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
#include "audio_core/null_sink.h" #include "audio_core/null_sink.h"
#include "audio_core/sink.h" #include "audio_core/sink.h"
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/dsp_dsp.h" #include "core/hle/service/dsp_dsp.h"
@ -42,10 +40,18 @@ void Init() {
} }
void AddAddressSpace(Kernel::VMManager& address_space) { void AddAddressSpace(Kernel::VMManager& address_space) {
auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r0_vma = address_space
.MapBackingMemory(DSP::HLE::region0_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r1_vma = address_space
.MapBackingMemory(DSP::HLE::region1_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
} }
@ -58,9 +64,9 @@ void SelectSink(std::string sink_id) {
return; return;
} }
auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { auto iter =
return sink_detail.id == sink_id; std::find_if(g_sink_details.begin(), g_sink_details.end(),
}); [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
if (iter == g_sink_details.end()) { if (iter == g_sink_details.end()) {
LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id");

View file

@ -12,7 +12,7 @@ class VMManager;
namespace AudioCore { namespace AudioCore {
constexpr int native_sample_rate = 32728; ///< 32kHz constexpr int native_sample_rate = 32728; ///< 32kHz
/// Initialise Audio Core /// Initialise Audio Core
void Init(); void Init();

View file

@ -6,31 +6,32 @@
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
namespace Codec { namespace Codec {
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients. // GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each. // Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long. // Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8; constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14; constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
StereoBuffer16 ret(ret_size); StereoBuffer16 ret(ret_size);
int yn1 = state.yn1, int yn1 = state.yn1, yn2 = state.yn2;
yn2 = state.yn2;
const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) { for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN]; const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF); const int scale = 1 << (frame_header & 0xF);
@ -43,7 +44,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
// Decodes an audio sample. One nibble produces one sample. // Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 { const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale; const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back. // We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point. // 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
@ -82,7 +84,8 @@ static s16 SignExtendS8(u8 x) {
return static_cast<s16>(static_cast<s8>(x)); return static_cast<s16>(static_cast<s8>(x));
} }
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
return ret; return ret;
} }
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co
return ret; return ret;
} }
}; };

View file

@ -6,7 +6,6 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace Codec { namespace Codec {
@ -29,7 +28,8 @@ struct ADPCMState {
* @param state ADPCM state, this is updated with new state * @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -37,7 +37,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -45,6 +46,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count);
}; };

View file

@ -6,30 +6,28 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
constexpr int num_sources = 24; constexpr int num_sources = 24;
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
/// The DSP is quadraphonic internally. /// The DSP is quadraphonic internally.
using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
/** /**
* This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
* FilterT::ProcessSample is called sequentially on the samples. * FilterT::ProcessSample is called sequentially on the samples.
*/ */
template<typename FrameT, typename FilterT> template <typename FrameT, typename FilterT>
void FilterFrame(FrameT& frame, FilterT& filter) { void FilterFrame(FrameT& frame, FilterT& filter) {
std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { std::transform(frame.begin(), frame.end(), frame.begin(),
return filter.ProcessSample(sample); [&filter](const auto& sample) { return filter.ProcessSample(sample); });
});
} }
} // namespace HLE } // namespace HLE

View file

@ -4,7 +4,6 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/mixers.h" #include "audio_core/hle/mixers.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
@ -47,10 +46,9 @@ static SharedMemory& WriteRegion() {
// Audio processing and mixing // Audio processing and mixing
static std::array<Source, num_sources> sources = { static std::array<Source, num_sources> sources = {
Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7),
Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23),
Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
}; };
static Mixers mixers; static Mixers mixers;
@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() {
// Generate intermediate mixes // Generate intermediate mixes
for (size_t i = 0; i < num_sources; i++) { for (size_t i = 0; i < num_sources; i++) {
write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); write.source_statuses.status[i] =
sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
for (size_t mix = 0; mix < 3; mix++) { for (size_t mix = 0; mix < 3; mix++) {
sources[i].MixInto(intermediate_mixes[mix], mix); sources[i].MixInto(intermediate_mixes[mix], mix);
} }
} }
// Generate final mix // Generate final mix
write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
write.intermediate_mix_samples, intermediate_mixes);
StereoFrame16 output_frame = mixers.GetOutput(); StereoFrame16 output_frame = mixers.GetOutput();
@ -152,7 +152,8 @@ void Shutdown() {
bool Tick() { bool Tick() {
StereoFrame16 current_frame = {}; StereoFrame16 current_frame = {};
// TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
// shared memory region)
current_frame = GenerateCurrentFrame(); current_frame = GenerateCurrentFrame();
OutputCurrentFrame(current_frame); OutputCurrentFrame(current_frame);

View file

@ -8,9 +8,7 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -23,15 +21,15 @@ class Sink;
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
// The application-accessible region of DSP memory consists of two parts. // The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
// Both are marked as IO and have Read/Write permissions. // have Read/Write permissions.
// //
// First Region: 0x1FF50000 (Size: 0x8000) // First Region: 0x1FF50000 (Size: 0x8000)
// Second Region: 0x1FF70000 (Size: 0x8000) // Second Region: 0x1FF70000 (Size: 0x8000)
// //
// The DSP reads from each region alternately based on the frame counter for each region much like a // The DSP reads from each region alternately based on the frame counter for each region much like a
// double-buffer. The frame counter is located as the very last u16 of each region and is incremented // double-buffer. The frame counter is located as the very last u16 of each region and is
// each audio tick. // incremented each audio tick.
constexpr VAddr region0_base = 0x1FF50000; constexpr VAddr region0_base = 0x1FF50000;
constexpr VAddr region1_base = 0x1FF70000; constexpr VAddr region1_base = 0x1FF70000;
@ -56,6 +54,7 @@ struct u32_dsp {
void operator=(u32 new_value) { void operator=(u32 new_value) {
storage = Convert(new_value); storage = Convert(new_value);
} }
private: private:
static constexpr u32 Convert(u32 value) { static constexpr u32 Convert(u32 value) {
return (value << 16) | (value >> 16); return (value << 16) | (value >> 16);
@ -89,13 +88,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
// See also: DSP::HLE::PipeRead. // See also: DSP::HLE::PipeRead.
// //
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
// not fixed in stone. The addresses above are only an examplar; they're what this implementation // are not fixed in stone. The addresses above are only an examplar; they're what this
// does and provides to applications. // implementation does and provides to applications.
// //
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the // Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the // the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
// second region via: // the second region via:
// second_region_dsp_addr = first_region_dsp_addr | 0x10000 // second_region_dsp_addr = first_region_dsp_addr | 0x10000
// //
// Applications maintain most of its own audio state, the memory region is used mainly for // Applications maintain most of its own audio state, the memory region is used mainly for
@ -103,21 +102,24 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// //
// In the documentation below, filter and effect transfer functions are specified in the z domain. // In the documentation below, filter and effect transfer functions are specified in the z domain.
// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital // (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
// frequency domain, just like how the s domain is the analog frequency domain.) // frequency domain, just like how the s domain is the analog frequency domain.)
#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words)) #define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
// GCC versions < 5.0 do not implement std::is_trivially_copyable. // GCC versions < 5.0 do not implement std::is_trivially_copyable.
// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
#if (__GNUC__ >= 5) || defined(__clang__) #if (__GNUC__ >= 5) || defined(__clang__)
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ "DSP structure " #name " doesn't use standard layout"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) static_assert(std::is_trivially_copyable<name>::value, \
"DSP structure " #name " isn't trivially copyable"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#else #else
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) "DSP structure " #name " doesn't use standard layout"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#endif #endif
struct SourceConfiguration { struct SourceConfiguration {
@ -130,7 +132,8 @@ struct SourceConfiguration {
BitField<0, 1, u32_le> format_dirty; BitField<0, 1, u32_le> format_dirty;
BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<1, 1, u32_le> mono_or_stereo_dirty;
BitField<2, 1, u32_le> adpcm_coefficients_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty;
BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. /// Tends to be set when a looped buffer is queued.
BitField<3, 1, u32_le> partial_embedded_buffer_dirty;
BitField<4, 1, u32_le> partial_reset_flag; BitField<4, 1, u32_le> partial_reset_flag;
BitField<16, 1, u32_le> enable_dirty; BitField<16, 1, u32_le> enable_dirty;
@ -138,7 +141,8 @@ struct SourceConfiguration {
BitField<18, 1, u32_le> rate_multiplier_dirty; BitField<18, 1, u32_le> rate_multiplier_dirty;
BitField<19, 1, u32_le> buffer_queue_dirty; BitField<19, 1, u32_le> buffer_queue_dirty;
BitField<20, 1, u32_le> loop_related_dirty; BitField<20, 1, u32_le> loop_related_dirty;
BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated. /// Tends to also be set when embedded buffer is updated.
BitField<21, 1, u32_le> play_position_dirty;
BitField<22, 1, u32_le> filters_enabled_dirty; BitField<22, 1, u32_le> filters_enabled_dirty;
BitField<23, 1, u32_le> simple_filter_dirty; BitField<23, 1, u32_le> simple_filter_dirty;
BitField<24, 1, u32_le> biquad_filter_dirty; BitField<24, 1, u32_le> biquad_filter_dirty;
@ -153,9 +157,9 @@ struct SourceConfiguration {
// Gain control // Gain control
/** /**
* Gain is between 0.0-1.0. This determines how much will this source appear on * Gain is between 0.0-1.0. This determines how much will this source appear on each of the
* each of the 12 channels that feed into the intermediate mixers. * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
* Each of the three intermediate mixers is fed two left and two right channels. * is fed two left and two right channels.
*/ */
float_le gain[3][4]; float_le gain[3][4];
@ -167,7 +171,7 @@ struct SourceConfiguration {
enum class InterpolationMode : u8 { enum class InterpolationMode : u8 {
Polyphase = 0, Polyphase = 0,
Linear = 1, Linear = 1,
None = 2 None = 2,
}; };
InterpolationMode interpolation_mode; InterpolationMode interpolation_mode;
@ -191,8 +195,8 @@ struct SourceConfiguration {
* This is a normalised biquad filter (second-order). * This is a normalised biquad filter (second-order).
* The transfer function of this filter is: * The transfer function of this filter is:
* H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
* Nintendo chose to negate the feedbackward coefficients. This differs from standard notation * Nintendo chose to negate the feedbackward coefficients. This differs from standard
* as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
* Values are signed fixed point with 14 fractional bits. * Values are signed fixed point with 14 fractional bits.
*/ */
struct BiquadFilter { struct BiquadFilter {
@ -239,23 +243,24 @@ struct SourceConfiguration {
/// Is a looping buffer. /// Is a looping buffer.
u8 is_looping; u8 is_looping;
/// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished. /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
/// This allows the emulated application to tell what buffer is currently playing /// finished. This allows the emulated application to tell what buffer is currently
/// playing.
u16_le buffer_id; u16_le buffer_id;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
}; };
u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i]) u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
Buffer buffers[4]; ///< Queued Buffers Buffer buffers[4]; ///< Queued Buffers
// Playback controls // Playback controls
u32_dsp loop_related; u32_dsp loop_related;
u8 enable; u8 enable;
INSERT_PADDING_BYTES(1); INSERT_PADDING_BYTES(1);
u16_le sync; ///< Application-side sync (See also: SourceStatus::sync) u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
u32_dsp play_position; ///< Position. (Units: number of samples) u32_dsp play_position; ///< Position. (Units: number of samples)
INSERT_PADDING_DSPWORDS(2); INSERT_PADDING_DSPWORDS(2);
// Embedded Buffer // Embedded Buffer
@ -270,13 +275,13 @@ struct SourceConfiguration {
enum class MonoOrStereo : u16_le { enum class MonoOrStereo : u16_le {
Mono = 1, Mono = 1,
Stereo = 2 Stereo = 2,
}; };
enum class Format : u16_le { enum class Format : u16_le {
PCM8 = 0, PCM8 = 0,
PCM16 = 1, PCM16 = 1,
ADPCM = 2 ADPCM = 2,
}; };
union { union {
@ -299,10 +304,11 @@ struct SourceConfiguration {
union { union {
u16_le flags2_raw; u16_le flags2_raw;
BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed? BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
}; };
/// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer). /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
/// buffer).
u16_le buffer_id; u16_le buffer_id;
}; };
@ -313,11 +319,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
struct SourceStatus { struct SourceStatus {
struct Status { struct Status {
u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
u32_dsp buffer_position; ///< Number of samples into the current buffer u32_dsp buffer_position; ///< Number of samples into the current buffer
u16_le current_buffer_id; ///< Updated when a buffer finishes playing u16_le current_buffer_id; ///< Updated when a buffer finishes playing
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
}; };
@ -347,7 +353,8 @@ struct DspConfiguration {
BitField<28, 1, u32_le> headphones_connected_dirty; BitField<28, 1, u32_le> headphones_connected_dirty;
}; };
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
/// each at the final mixer.
float_le volume[3]; float_le volume[3];
INSERT_PADDING_DSPWORDS(3); INSERT_PADDING_DSPWORDS(3);
@ -355,7 +362,7 @@ struct DspConfiguration {
enum class OutputFormat : u16_le { enum class OutputFormat : u16_le {
Mono = 0, Mono = 0,
Stereo = 1, Stereo = 1,
Surround = 2 Surround = 2,
}; };
OutputFormat output_format; OutputFormat output_format;
@ -388,8 +395,10 @@ struct DspConfiguration {
u16_le enable; u16_le enable;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
u16_le outputs; u16_le outputs;
u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer. /// The application allocates a block of memory for the DSP to use as a work buffer.
u16_le frame_count; ///< Frames to delay by u32_dsp work_buffer_address;
/// Frames to delay by
u16_le frame_count;
// Coefficients // Coefficients
s16_le g; ///< Fixed point with 7 fractional bits s16_le g; ///< Fixed point with 7 fractional bits
@ -506,21 +515,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
extern std::array<SharedMemory, 2> g_regions; extern std::array<SharedMemory, 2> g_regions;
// Structures must have an offset that is a multiple of two. // Structures must have an offset that is a multiple of two.
static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0,
static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0,
static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, final_samples) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
#undef INSERT_PADDING_DSPWORDS #undef INSERT_PADDING_DSPWORDS
#undef ASSERT_DSP_STRUCT #undef ASSERT_DSP_STRUCT

View file

@ -4,11 +4,9 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/filter.h" #include "audio_core/hle/filter.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() {
b0 = 1 << 15; b0 = 1 << 15;
} }
void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { void SourceFilters::SimpleFilter::Configure(
SourceConfiguration::Configuration::SimpleFilter config) {
a1 = config.a1; a1 = config.a1;
b0 = config.b0; b0 = config.b0;
} }
@ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() {
b0 = 1 << 14; b0 = 1 << 14;
} }
void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { void SourceFilters::BiquadFilter::Configure(
SourceConfiguration::Configuration::BiquadFilter config) {
a1 = config.a1; a1 = config.a1;
a2 = config.a2; a2 = config.a2;
b0 = config.b0; b0 = config.b0;

View file

@ -5,10 +5,8 @@
#pragma once #pragma once
#include <array> #include <array>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -17,7 +15,9 @@ namespace HLE {
/// Preprocessing filters. There is an independent set of filters for each Source. /// Preprocessing filters. There is an independent set of filters for each Source.
class SourceFilters final { class SourceFilters final {
public: public:
SourceFilters() { Reset(); } SourceFilters() {
Reset();
}
/// Reset internal state. /// Reset internal state.
void Reset(); void Reset();
@ -54,7 +54,9 @@ private:
bool biquad_filter_enabled; bool biquad_filter_enabled;
struct SimpleFilter { struct SimpleFilter {
SimpleFilter() { Reset(); } SimpleFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();
@ -80,7 +82,9 @@ private:
} simple_filter; } simple_filter;
struct BiquadFilter { struct BiquadFilter {
BiquadFilter() { Reset(); } BiquadFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();

View file

@ -7,7 +7,6 @@
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/mixers.h" #include "audio_core/hle/mixers.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -20,11 +19,9 @@ void Mixers::Reset() {
state = {}; state = {};
} }
DspStatus Mixers::Tick(DspConfiguration& config, DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples, IntermediateMixSamples& write_samples,
IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) {
const std::array<QuadFrame32, 3>& input)
{
ParseConfig(config); ParseConfig(config);
AuxReturn(read_samples); AuxReturn(read_samples);
@ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) {
if (config.output_format_dirty) { if (config.output_format_dirty) {
config.output_format_dirty.Assign(0); config.output_format_dirty.Assign(0);
state.output_format = config.output_format; state.output_format = config.output_format;
LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
static_cast<size_t>(config.output_format));
} }
if (config.headphones_connected_dirty) { if (config.headphones_connected_dirty) {
config.headphones_connected_dirty.Assign(0); config.headphones_connected_dirty.Assign(0);
// Do nothing. // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
// (Note: Whether headphones are connected does affect coefficients used for surround sound.) // surround sound.)
LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
} }
@ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) {
return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
} }
static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
return { const std::array<s16, 2>& b) {
ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
};
} }
void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
@ -106,27 +103,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
switch (state.output_format) { switch (state.output_format) {
case OutputFormat::Mono: case OutputFormat::Mono:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
[gain](const std::array<s16, 2>& accumulator,
const std::array<s32, 4>& sample) -> std::array<s16, 2> {
// Downmix to mono // Downmix to mono
s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); s16 mono = ClampToS16(static_cast<s32>(
(gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
2));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { mono, mono }); return AddAndClampToS16(accumulator, {mono, mono});
}); });
return; return;
case OutputFormat::Surround: case OutputFormat::Surround:
// TODO(merry): Implement surround sound. // TODO(merry): Implement surround sound.
// fallthrough // fallthrough
case OutputFormat::Stereo: case OutputFormat::Stereo:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
[gain](const std::array<s16, 2>& accumulator,
const std::array<s32, 4>& sample) -> std::array<s16, 2> {
// Downmix to stereo // Downmix to stereo
s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { left, right }); return AddAndClampToS16(accumulator, {left, right});
}); });
return; return;
} }
@ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
} }
void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
// QuadFrame32.
if (state.mixer1_enabled) { if (state.mixer1_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; state.intermediate_mix_buffer[1][sample][channel] =
read_samples.mix1.pcm32[channel][sample];
} }
} }
} }
@ -148,14 +153,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
if (state.mixer2_enabled) { if (state.mixer2_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; state.intermediate_mix_buffer[2][sample][channel] =
read_samples.mix2.pcm32[channel][sample];
} }
} }
} }
} }
void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { void Mixers::AuxSend(IntermediateMixSamples& write_samples,
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. const std::array<QuadFrame32, 3>& input) {
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
// QuadFrame32.
state.intermediate_mix_buffer[0] = input[0]; state.intermediate_mix_buffer[0] = input[0];
@ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() {
current_frame.fill({}); current_frame.fill({});
for (size_t mix = 0; mix < 3; mix++) { for (size_t mix = 0; mix < 3; mix++) {
DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
state.intermediate_mix_buffer[mix]);
} }
// TODO(merry): Compressor. (We currently assume a disabled compressor.) // TODO(merry): Compressor. (We currently assume a disabled compressor.)

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
@ -20,10 +19,8 @@ public:
void Reset(); void Reset();
DspStatus Tick(DspConfiguration& config, DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples, IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
IntermediateMixSamples& write_samples,
const std::array<QuadFrame32, 3>& input);
StereoFrame16 GetOutput() const { StereoFrame16 GetOutput() const {
return current_frame; return current_frame;
@ -53,7 +50,8 @@ private:
void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
/// INTERNAL: Mix current_frame. /// INTERNAL: Mix current_frame.
void MixCurrentFrame(); void MixCurrentFrame();
/// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
/// into current_frame.
void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
/// INTERNAL: Generate DspStatus based on internal state. /// INTERNAL: Generate DspStatus based on internal state.
DspStatus GetCurrentStatus() const; DspStatus GetCurrentStatus() const;

View file

@ -4,14 +4,11 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/pipe.h" #include "audio_core/hle/pipe.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/service/dsp_dsp.h" #include "core/hle/service/dsp_dsp.h"
namespace DSP { namespace DSP {
@ -44,8 +41,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
std::vector<u8>& data = pipe_data[pipe_index]; std::vector<u8>& data = pipe_data[pipe_index];
if (length > data.size()) { if (length > data.size()) {
LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", LOG_WARNING(
pipe_index, length, data.size()); Audio_DSP,
"pipe_number = %zu is out of data, application requested read of %u but %zu remain",
pipe_index, length, data.size());
length = static_cast<u32>(data.size()); length = static_cast<u32>(data.size());
} }
@ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() {
0x8000 + offsetof(SharedMemory, unknown11) / 2, 0x8000 + offsetof(SharedMemory, unknown11) / 2,
0x8000 + offsetof(SharedMemory, unknown12) / 2, 0x8000 + offsetof(SharedMemory, unknown12) / 2,
0x8000 + offsetof(SharedMemory, unknown13) / 2, 0x8000 + offsetof(SharedMemory, unknown13) / 2,
0x8000 + offsetof(SharedMemory, unknown14) / 2 0x8000 + offsetof(SharedMemory, unknown14) / 2,
}; };
// Begin with a u16 denoting the number of structs. // Begin with a u16 denoting the number of structs.
@ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
switch (pipe_number) { switch (pipe_number) {
case DspPipe::Audio: { case DspPipe::Audio: {
if (buffer.size() != 4) { if (buffer.size() != 4) {
LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written",
buffer.size());
return; return;
} }
@ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
Initalize = 0, Initalize = 0,
Shutdown = 1, Shutdown = 1,
Wakeup = 2, Wakeup = 2,
Sleep = 3 Sleep = 3,
}; };
// The difference between Initialize and Wakeup is that Input state is maintained // The difference between Initialize and Wakeup is that Input state is maintained
@ -152,7 +152,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
dsp_state = DspState::Sleeping; dsp_state = DspState::Sleeping;
break; break;
default: default:
LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); LOG_ERROR(Audio_DSP,
"Application has requested unknown state transition of DSP hardware %hhu",
buffer[0]);
dsp_state = DspState::Off; dsp_state = DspState::Off;
break; break;
} }
@ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
return; return;
} }
default: default:
LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented",
static_cast<size_t>(pipe_number));
UNIMPLEMENTED(); UNIMPLEMENTED();
return; return;
} }

View file

@ -6,7 +6,6 @@
#include <cstddef> #include <cstddef>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -19,16 +18,18 @@ enum class DspPipe {
Debug = 0, Debug = 0,
Dma = 1, Dma = 1,
Audio = 2, Audio = 2,
Binary = 3 Binary = 3,
}; };
constexpr size_t NUM_DSP_PIPE = 8; constexpr size_t NUM_DSP_PIPE = 8;
/** /**
* Reads `length` bytes from the DSP pipe identified with `pipe_number`. * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
* @note Can read up to the maximum value of a u16 in bytes (65,535). * @note Can read up to the maximum value of a u16 in bytes (65,535).
* @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty
* vector will be returned.
* @note IF `length` is set to 0, an empty vector will be returned. * @note IF `length` is set to 0, an empty vector will be returned.
* @note IF `length` is greater than the amount of data available, this function will only read the available amount. * @note IF `length` is greater than the amount of data available, this function will only read the
* available amount.
* @param pipe_number a `DspPipe` * @param pipe_number a `DspPipe`
* @param length the number of bytes to read. The max is 65,535 (max of u16). * @param length the number of bytes to read. The max is 65,535 (max of u16).
* @returns a vector of bytes from the specified pipe. On error, will be empty. * @returns a vector of bytes from the specified pipe. On error, will be empty.
@ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
enum class DspState { enum class DspState {
Off, Off,
On, On,
Sleeping Sleeping,
}; };
/// Get the state of the DSP /// Get the state of the DSP
DspState GetDspState(); DspState GetDspState();

View file

@ -4,21 +4,19 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/source.h" #include "audio_core/hle/source.h"
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/memory.h" #include "core/memory.h"
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
ParseConfig(config, adpcm_coeffs); ParseConfig(config, adpcm_coeffs);
if (state.enabled) { if (state.enabled) {
@ -47,7 +45,8 @@ void Source::Reset() {
state = {}; state = {};
} }
void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { void Source::ParseConfig(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
if (!config.dirty_raw) { if (!config.dirty_raw) {
return; return;
} }
@ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
if (state.rate_multiplier <= 0) { if (state.rate_multiplier <= 0) {
LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f",
source_id, state.rate_multiplier);
state.rate_multiplier = 1.0f; state.rate_multiplier = 1.0f;
// Note: Actual firmware starts producing garbage if this occurs. // Note: Actual firmware starts producing garbage if this occurs.
} }
@ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.adpcm_coefficients_dirty) { if (config.adpcm_coefficients_dirty) {
config.adpcm_coefficients_dirty.Assign(0); config.adpcm_coefficients_dirty.Assign(0);
std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(),
[](const auto& coeff) { return static_cast<s16>(coeff); }); state.adpcm_coeffs.begin(),
[](const auto& coeff) { return static_cast<s16>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
} }
if (config.gain_0_dirty) { if (config.gain_0_dirty) {
config.gain_0_dirty.Assign(0); config.gain_0_dirty.Assign(0);
std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
} }
if (config.gain_1_dirty) { if (config.gain_1_dirty) {
config.gain_1_dirty.Assign(0); config.gain_1_dirty.Assign(0);
std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
} }
if (config.gain_2_dirty) { if (config.gain_2_dirty) {
config.gain_2_dirty.Assign(0); config.gain_2_dirty.Assign(0);
std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
} }
if (config.filters_enabled_dirty) { if (config.filters_enabled_dirty) {
config.filters_enabled_dirty.Assign(0); config.filters_enabled_dirty.Assign(0);
state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); state.filters.Enable(config.simple_filter_enabled.ToBool(),
LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", config.biquad_filter_enabled.ToBool());
source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id,
config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
} }
if (config.simple_filter_dirty) { if (config.simple_filter_dirty) {
@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.interpolation_dirty) { if (config.interpolation_dirty) {
config.interpolation_dirty.Assign(0); config.interpolation_dirty.Assign(0);
state.interpolation_mode = config.interpolation_mode; state.interpolation_mode = config.interpolation_mode;
LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id,
static_cast<size_t>(state.interpolation_mode));
} }
if (config.format_dirty || config.embedded_buffer_dirty) { if (config.format_dirty || config.embedded_buffer_dirty) {
config.format_dirty.Assign(0); config.format_dirty.Assign(0);
state.format = config.format; state.format = config.format;
LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id,
static_cast<size_t>(state.format));
} }
if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
config.mono_or_stereo_dirty.Assign(0); config.mono_or_stereo_dirty.Assign(0);
state.mono_or_stereo = config.mono_or_stereo; state.mono_or_stereo = config.mono_or_stereo;
LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id,
static_cast<size_t>(state.mono_or_stereo));
} }
if (config.embedded_buffer_dirty) { if (config.embedded_buffer_dirty) {
@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
config.physical_address, config.physical_address,
config.length, config.length,
static_cast<u8>(config.adpcm_ps), static_cast<u8>(config.adpcm_ps),
{ config.adpcm_yn[0], config.adpcm_yn[1] }, {config.adpcm_yn[0], config.adpcm_yn[1]},
config.adpcm_dirty.ToBool(), config.adpcm_dirty.ToBool(),
config.is_looping.ToBool(), config.is_looping.ToBool(),
config.buffer_id, config.buffer_id,
state.mono_or_stereo, state.mono_or_stereo,
state.format, state.format,
false false,
}); });
LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu",
config.physical_address, config.length, config.buffer_id);
} }
if (config.buffer_queue_dirty) { if (config.buffer_queue_dirty) {
@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
b.physical_address, b.physical_address,
b.length, b.length,
static_cast<u8>(b.adpcm_ps), static_cast<u8>(b.adpcm_ps),
{ b.adpcm_yn[0], b.adpcm_yn[1] }, {b.adpcm_yn[0], b.adpcm_yn[1]},
b.adpcm_dirty != 0, b.adpcm_dirty != 0,
b.is_looping != 0, b.is_looping != 0,
b.buffer_id, b.buffer_id,
state.mono_or_stereo, state.mono_or_stereo,
state.format, state.format,
true true,
}); });
LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i,
b.physical_address, b.length, b.buffer_id);
} }
} }
config.buffers_dirty = 0; config.buffers_dirty = 0;
@ -218,10 +225,13 @@ void Source::GenerateFrame() {
break; break;
} }
const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); const size_t size_to_copy =
std::min(state.current_buffer.size(), current_frame.size() - frame_position);
std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy,
state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); current_frame.begin() + frame_position);
state.current_buffer.erase(state.current_buffer.begin(),
state.current_buffer.begin() + size_to_copy);
frame_position += size_to_copy; frame_position += size_to_copy;
state.next_sample_number += static_cast<u32>(size_to_copy); state.next_sample_number += static_cast<u32>(size_to_copy);
@ -230,9 +240,9 @@ void Source::GenerateFrame() {
state.filters.ProcessFrame(current_frame); state.filters.ProcessFrame(current_frame);
} }
bool Source::DequeueBuffer() { bool Source::DequeueBuffer() {
ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); ASSERT_MSG(state.current_buffer.empty(),
"Shouldn't dequeue; we still have data in current_buffer");
if (state.input_queue.empty()) if (state.input_queue.empty())
return false; return false;
@ -261,29 +271,34 @@ bool Source::DequeueBuffer() {
break; break;
case Format::ADPCM: case Format::ADPCM:
DEBUG_ASSERT(num_channels == 1); DEBUG_ASSERT(num_channels == 1);
state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); state.current_buffer =
Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }
} else { } else {
LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", LOG_WARNING(Audio_DSP,
source_id, buf.buffer_id, buf.length, buf.physical_address); "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
source_id, buf.buffer_id, buf.length, buf.physical_address);
state.current_buffer.clear(); state.current_buffer.clear();
return true; return true;
} }
switch (state.interpolation_mode) { switch (state.interpolation_mode) {
case InterpolationMode::None: case InterpolationMode::None:
state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Linear: case InterpolationMode::Linear:
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Polyphase: case InterpolationMode::Polyphase:
// TODO(merry): Implement polyphase interpolation // TODO(merry): Implement polyphase interpolation
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
@ -296,7 +311,8 @@ bool Source::DequeueBuffer() {
state.buffer_update = buf.from_queue; state.buffer_update = buf.from_queue;
LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); source_id, buf.buffer_id, buf.from_queue ? "true" : "false",
state.current_buffer.size());
return true; return true;
} }

View file

@ -7,13 +7,11 @@
#include <array> #include <array>
#include <queue> #include <queue>
#include <vector> #include <vector>
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "audio_core/hle/common.h" #include "audio_core/hle/common.h"
#include "audio_core/hle/dsp.h" #include "audio_core/hle/dsp.h"
#include "audio_core/hle/filter.h" #include "audio_core/hle/filter.h"
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace DSP { namespace DSP {
@ -40,13 +38,17 @@ public:
/** /**
* This is called once every audio frame. This performs per-source processing every frame. * This is called once every audio frame. This performs per-source processing every frame.
* @param config The new configuration we've got for this Source from the application. * @param config The new configuration we've got for this Source from the application.
* @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain
* @return The current status of this Source. This is given back to the emulated application via SharedMemory. * invalid values otherwise).
* @return The current status of this Source. This is given back to the emulated application via
* SharedMemory.
*/ */
SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); SourceStatus::Status Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]);
/** /**
* Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th
* intermediate mixer.
* @param dest The QuadFrame32 to mix into. * @param dest The QuadFrame32 to mix into.
* @param intermediate_mix_id The id of the intermediate mix whose gains we are using. * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
*/ */
@ -77,7 +79,7 @@ private:
}; };
struct BufferOrder { struct BufferOrder {
bool operator() (const Buffer& a, const Buffer& b) const { bool operator()(const Buffer& a, const Buffer& b) const {
// Lower buffer_id comes first. // Lower buffer_id comes first.
return a.buffer_id > b.buffer_id; return a.buffer_id > b.buffer_id;
} }
@ -134,7 +136,8 @@ private:
void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
/// INTERNAL: Generate the current audio output for this frame based on our internal state. /// INTERNAL: Generate the current audio output for this frame based on our internal state.
void GenerateFrame(); void GenerateFrame();
/// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer. /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it
/// into current_buffer.
bool DequeueBuffer(); bool DequeueBuffer();
/// INTERNAL: Generates a SourceStatus::Status based on our internal state. /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
SourceStatus::Status GetCurrentStatus(); SourceStatus::Status GetCurrentStatus();

View file

@ -3,7 +3,6 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "audio_core/interpolate.h" #include "audio_core/interpolate.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -17,7 +16,8 @@ constexpr u64 scale_mask = scale_factor - 1;
/// Here we step over the input in steps of rate_multiplier, until we consume all of the input. /// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
/// Three adjacent samples are passed to fn each step. /// Three adjacent samples are passed to fn each step.
template <typename Function> template <typename Function>
static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
float rate_multiplier, Function fn) {
ASSERT(rate_multiplier > 0); ASSERT(rate_multiplier > 0);
if (input.size() < 2) if (input.size() < 2)
@ -63,23 +63,24 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
} }
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) {
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(
return x0; state, input, rate_multiplier,
}); [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; });
} }
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) {
// Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(state, input, rate_multiplier,
// This is a saturated subtraction. (Verified by black-box fuzzing.) [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) {
s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); // This is a saturated subtraction. (Verified by black-box fuzzing.)
s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
return std::array<s16, 2> { return std::array<s16, 2>{
static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
static_cast<s16>(x0[1] + fraction * delta1 / scale_factor) static_cast<s16>(x0[1] + fraction * delta1 / scale_factor),
}; };
}); });
} }
} // namespace AudioInterp } // namespace AudioInterp

View file

@ -6,7 +6,6 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioInterp { namespace AudioInterp {
@ -24,7 +23,8 @@ struct State {
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier);
@ -33,7 +33,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/sink.h" #include "audio_core/sink.h"

View file

@ -3,16 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <list> #include <list>
#include <numeric>
#include <vector> #include <vector>
#include <SDL.h> #include <SDL.h>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <numeric>
namespace AudioCore { namespace AudioCore {
@ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
SDL_AudioSpec obtained_audiospec; SDL_AudioSpec obtained_audiospec;
SDL_zero(obtained_audiospec); SDL_zero(obtained_audiospec);
impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); impl->audio_device_id =
SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
if (impl->audio_device_id <= 0) { if (impl->audio_device_id <= 0) {
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
return; return;
@ -86,11 +84,12 @@ size_t SDL2Sink::SamplesInQueue() const {
SDL_LockAudioDevice(impl->audio_device_id); SDL_LockAudioDevice(impl->audio_device_id);
size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(),
[](size_t sum, const auto& buffer) { static_cast<size_t>(0), [](size_t sum, const auto& buffer) {
// Division by two because each stereo sample is made of two s16. // Division by two because each stereo sample is made of
return sum + buffer.size() / 2; // two s16.
}); return sum + buffer.size() / 2;
});
SDL_UnlockAudioDevice(impl->audio_device_id); SDL_UnlockAudioDevice(impl->audio_device_id);
@ -100,7 +99,8 @@ size_t SDL2Sink::SamplesInQueue() const {
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
Impl* impl = reinterpret_cast<Impl*>(impl_); Impl* impl = reinterpret_cast<Impl*>(impl_);
size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) /
sizeof(s16); // Keep track of size in 16-bit increments.
while (remaining_size > 0 && !impl->queue.empty()) { while (remaining_size > 0 && !impl->queue.empty()) {
if (impl->queue.front().size() <= remaining_size) { if (impl->queue.front().size() <= remaining_size) {
@ -111,7 +111,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes)
} else { } else {
memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
buffer += remaining_size * sizeof(s16); buffer += remaining_size * sizeof(s16);
impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); impl->queue.front().erase(impl->queue.front().begin(),
impl->queue.front().begin() + remaining_size);
remaining_size = 0; remaining_size = 0;
} }
} }

View file

@ -6,7 +6,6 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include "audio_core/sink.h" #include "audio_core/sink.h"
namespace AudioCore { namespace AudioCore {

View file

@ -5,20 +5,21 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore { namespace AudioCore {
/** /**
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
* Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
* They are dumb outputs.
*/ */
class Sink { class Sink {
public: public:
virtual ~Sink() = default; virtual ~Sink() = default;
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units:
/// samples/sec)
virtual unsigned int GetNativeSampleRate() const = 0; virtual unsigned int GetNativeSampleRate() const = 0;
/** /**

View file

@ -4,10 +4,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "audio_core/null_sink.h" #include "audio_core/null_sink.h"
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#endif #endif
@ -17,9 +15,9 @@ namespace AudioCore {
// g_sink_details is ordered in terms of desirability, with the best choice at the top. // g_sink_details is ordered in terms of desirability, with the best choice at the top.
const std::vector<SinkDetails> g_sink_details = { const std::vector<SinkDetails> g_sink_details = {
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
{ "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, {"sdl2", []() { return std::make_unique<SDL2Sink>(); }},
#endif #endif
{ "null", []() { return std::make_unique<NullSink>(); } }, {"null", []() { return std::make_unique<NullSink>(); }},
}; };
} // namespace AudioCore } // namespace AudioCore

View file

@ -5,12 +5,9 @@
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <vector> #include <vector>
#include <SoundTouch.h> #include <SoundTouch.h>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/time_stretch.h" #include "audio_core/time_stretch.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/math_util.h" #include "common/math_util.h"
@ -26,8 +23,8 @@ static double ClampRatio(double ratio) {
return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO);
} }
constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
constexpr double SMOOTHING_FACTOR = 0.007; constexpr double SMOOTHING_FACTOR = 0.007;
@ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
double ratio = CalculateCurrentRatio(); double ratio = CalculateCurrentRatio();
ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue);
impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; impl->smoothed_ratio =
(1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio;
impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio);
// SoundTouch's tempo definition the inverse of our ratio definition. // SoundTouch's tempo definition the inverse of our ratio definition.
@ -100,7 +98,8 @@ double TimeStretcher::CalculateCurrentRatio() {
const steady_clock::time_point now = steady_clock::now(); const steady_clock::time_point now = steady_clock::now();
const std::chrono::duration<double> duration = now - impl->frame_timer; const std::chrono::duration<double> duration = now - impl->frame_timer;
const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); const double expected_time =
static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate);
const double actual_time = duration.count(); const double actual_time = duration.count();
double ratio; double ratio;

View file

@ -5,7 +5,6 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore { namespace AudioCore {
@ -37,7 +36,8 @@ public:
/** /**
* Does audio stretching and produces the time-stretched samples. * Does audio stretching and produces the time-stretched samples.
* Timer calculations use sample_delay to determine how much of a margin we have. * Timer calculations use sample_delay to determine how much of a margin we have.
* @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. * @param sample_delay How many samples are buffered downstream of this module and haven't been
* played yet.
* @return Samples to play in interleaved stereo PCM16 format. * @return Samples to play in interleaved stereo PCM16 format.
*/ */
std::vector<s16> Process(size_t sample_delay); std::vector<s16> Process(size_t sample_delay);
@ -48,7 +48,8 @@ private:
/// INTERNAL: ratio = wallclock time / emulated time /// INTERNAL: ratio = wallclock time / emulated time
double CalculateCurrentRatio(); double CalculateCurrentRatio();
/// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction. /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate
/// direction.
double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const;
/// INTERNAL: Gets the time-stretched samples from SoundTouch. /// INTERNAL: Gets the time-stretched samples from SoundTouch.
std::vector<s16> GetSamples(); std::vector<s16> GetSamples();

View file

@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <string>
#include <thread>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string>
#include <thread>
// This needs to be included before getopt.h because the latter #defines symbols used by it // This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h" #include "common/microprofile.h"
@ -13,53 +13,48 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#include <getopt.h> #include <getopt.h>
#else #else
#include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <unistd.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#endif #endif
#include "common/logging/log.h" #include "citra/config.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/core.h" #include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "citra/config.h" #include "core/system.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
static void PrintHelp(const char* argv0) {
static void PrintHelp(const char *argv0) std::cout << "Usage: " << argv0
{ << " [options] <filename>\n"
std::cout << "Usage: " << argv0 << " [options] <filename>\n"
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
"-h, --help Display this help and exit\n" "-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n"; "-v, --version Output version information and exit\n";
} }
static void PrintVersion() static void PrintVersion() {
{
std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
} }
/// Application entry point /// Application entry point
int main(int argc, char **argv) { int main(int argc, char** argv) {
Config config; Config config;
int option_index = 0; int option_index = 0;
bool use_gdbstub = Settings::values.use_gdbstub; bool use_gdbstub = Settings::values.use_gdbstub;
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
char *endarg; char* endarg;
#ifdef _WIN32 #ifdef _WIN32
int argc_w; int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@ -72,10 +67,10 @@ int main(int argc, char **argv) {
std::string boot_filename; std::string boot_filename;
static struct option long_options[] = { static struct option long_options[] = {
{ "gdbport", required_argument, 0, 'g' }, {"gdbport", required_argument, 0, 'g'},
{ "help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h'},
{ "version", no_argument, 0, 'v' }, {"version", no_argument, 0, 'v'},
{ 0, 0, 0, 0 } {0, 0, 0, 0},
}; };
while (optind < argc) { while (optind < argc) {
@ -86,7 +81,8 @@ int main(int argc, char **argv) {
errno = 0; errno = 0;
gdb_port = strtoul(optarg, &endarg, 0); gdb_port = strtoul(optarg, &endarg, 0);
use_gdbstub = true; use_gdbstub = true;
if (endarg == optarg) errno = EINVAL; if (endarg == optarg)
errno = EINVAL;
if (errno != 0) { if (errno != 0) {
perror("--gdbport"); perror("--gdbport");
exit(1); exit(1);

View file

@ -3,19 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <memory> #include <memory>
#include <inih/cpp/INIReader.h>
#include <SDL.h> #include <SDL.h>
#include <inih/cpp/INIReader.h>
#include "citra/default_ini.h" #include "citra/default_ini.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/settings.h"
#include "config.h" #include "config.h"
#include "core/settings.h"
Config::Config() { Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files. // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@ -45,15 +39,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
// directly mapped keys // directly mapped keys
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_L,
SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
// indirectly mapped keys // indirectly mapped keys
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
SDL_SCANCODE_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
@ -62,7 +54,8 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
} }
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); Settings::values.pad_circle_modifier_scale =
(float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
// Core // Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@ -71,19 +64,22 @@ void Config::ReadValues() {
// Renderer // Renderer
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_scaled_resolution =
sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false);
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0);
// Audio // Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); Settings::values.enable_audio_stretching =
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
// Data Storage // Data Storage
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System // System
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
@ -94,7 +90,8 @@ void Config::ReadValues() {
// Debugging // Debugging
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.gdbstub_port =
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
} }
void Config::Reload() { void Config::Reload() {

View file

@ -6,15 +6,15 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <inih/cpp/INIReader.h> #include <inih/cpp/INIReader.h>
class Config { class Config {
std::unique_ptr<INIReader> sdl2_config; std::unique_ptr<INIReader> sdl2_config;
std::string sdl2_config_loc; std::string sdl2_config_loc;
bool LoadINI(const std::string& default_contents="", bool retry=true); bool LoadINI(const std::string& default_contents = "", bool retry = true);
void ReadValues(); void ReadValues();
public: public:
Config(); Config();

View file

@ -104,5 +104,4 @@ log_filter = *:Info
use_gdbstub=false use_gdbstub=false
gdbstub_port=24689 gdbstub_port=24689
)"; )";
} }

View file

@ -5,22 +5,16 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <SDL.h> #include <SDL.h>
#include <glad/glad.h> #include <glad/glad.h>
#include "citra/emu_window/emu_window_sdl2.h"
#include "common/key_map.h" #include "common/key_map.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@ -40,9 +34,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
KeyMap::PressKey(*this, { key, keyboard_id }); KeyMap::PressKey(*this, {key, keyboard_id});
} else if (state == SDL_RELEASED) { } else if (state == SDL_RELEASED) {
KeyMap::ReleaseKey(*this, { key, keyboard_id }); KeyMap::ReleaseKey(*this, {key, keyboard_id});
} }
} }
@ -55,7 +49,8 @@ void EmuWindow_SDL2::OnResize() {
SDL_GetWindowSize(render_window, &width, &height); SDL_GetWindowSize(render_window, &width, &height);
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
EmuWindow_SDL2::EmuWindow_SDL2() { EmuWindow_SDL2::EmuWindow_SDL2() {
@ -80,12 +75,13 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
render_window = SDL_CreateWindow(window_title.c_str(), Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
render_window = SDL_CreateWindow(
window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position SDL_WINDOWPOS_UNDEFINED, // y position
VideoCore::kScreenTopWidth, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) { if (render_window == nullptr) {
@ -171,10 +167,14 @@ void EmuWindow_SDL2::DoneCurrent() {
void EmuWindow_SDL2::ReloadSetKeymaps() { void EmuWindow_SDL2::ReloadSetKeymaps() {
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) { void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
} }

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <utility> #include <utility>
#include "common/emu_window.h" #include "common/emu_window.h"
struct SDL_Window; struct SDL_Window;
@ -47,7 +46,8 @@ private:
void OnResize(); void OnResize();
/// Called when a configuration change affects the minimal size of the window /// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override; void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
/// Is the window still open? /// Is the window still open?
bool is_open = true; bool is_open = true;
@ -55,7 +55,7 @@ private:
/// Internal SDL2 render window /// Internal SDL2 render window
SDL_Window* render_window; SDL_Window* render_window;
using SDL_GLContext = void *; using SDL_GLContext = void*;
/// The OpenGL context associated with the window /// The OpenGL context associated with the window
SDL_GLContext gl_context; SDL_GLContext gl_context;

View file

@ -2,15 +2,15 @@
// Microsoft Visual C++ generated include file. // Microsoft Visual C++ generated include file.
// Used by pcafe.rc // Used by pcafe.rc
// //
#define IDI_ICON3 103 #define IDI_ICON3 103
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View file

@ -9,27 +9,23 @@
#endif #endif
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "common/key_map.h" #include "common/key_map.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
#define APP_NAME "citra" #define APP_NAME "citra"
#define APP_VERSION "0.1-" VERSION #define APP_VERSION "0.1-" VERSION
#define APP_TITLE APP_NAME " " APP_VERSION #define APP_TITLE APP_NAME " " APP_VERSION
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
EmuThread::EmuThread(GRenderWindow* render_window) : EmuThread::EmuThread(GRenderWindow* render_window)
exec_step(false), running(false), stop_run(false), render_window(render_window) { : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
}
void EmuThread::run() { void EmuThread::run() {
render_window->MakeCurrent(); render_window->MakeCurrent();
@ -64,7 +60,7 @@ void EmuThread::run() {
was_active = false; was_active = false;
} else { } else {
std::unique_lock<std::mutex> lock(running_mutex); std::unique_lock<std::mutex> lock(running_mutex);
running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
} }
} }
@ -78,14 +74,13 @@ void EmuThread::run() {
render_window->moveContext(); render_window->moveContext();
} }
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
// context.
// The corresponding functionality is handled in EmuThread instead // The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget class GGLWidgetInternal : public QGLWidget {
{
public: public:
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
: QGLWidget(fmt, parent), parent(parent) { : QGLWidget(fmt, parent), parent(parent) {}
}
void paintEvent(QPaintEvent* ev) override { void paintEvent(QPaintEvent* ev) override {
if (do_painting) { if (do_painting) {
@ -98,37 +93,43 @@ public:
parent->OnFramebufferSizeChanged(); parent->OnFramebufferSizeChanged();
} }
void DisablePainting() { do_painting = false; } void DisablePainting() {
void EnablePainting() { do_painting = true; } do_painting = false;
}
void EnablePainting() {
do_painting = true;
}
private: private:
GRenderWindow* parent; GRenderWindow* parent;
bool do_painting; bool do_painting;
}; };
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { : QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) {
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title)); setWindowTitle(QString::fromStdString(window_title));
keyboard_id = KeyMap::NewDeviceId(); keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps(); ReloadSetKeymaps();
} }
void GRenderWindow::moveContext() void GRenderWindow::moveContext() {
{
DoneCurrent(); DoneCurrent();
// We need to move GL context to the swapping thread in Qt5 // We need to move GL context to the swapping thread in Qt5
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
// If the thread started running, move the GL Context to the new thread. Otherwise, move it back. // If the thread started running, move the GL Context to the new thread. Otherwise, move it
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); // back.
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
? emu_thread
: qApp->thread();
child->context()->moveToThread(thread); child->context()->moveToThread(thread);
#endif #endif
} }
void GRenderWindow::SwapBuffers() void GRenderWindow::SwapBuffers() {
{
#if !defined(QT_NO_DEBUG) #if !defined(QT_NO_DEBUG)
// Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
// since the last time you called swapBuffers. This presumably means something if you're using // since the last time you called swapBuffers. This presumably means something if you're using
@ -139,54 +140,48 @@ void GRenderWindow::SwapBuffers()
child->swapBuffers(); child->swapBuffers();
} }
void GRenderWindow::MakeCurrent() void GRenderWindow::MakeCurrent() {
{
child->makeCurrent(); child->makeCurrent();
} }
void GRenderWindow::DoneCurrent() void GRenderWindow::DoneCurrent() {
{
child->doneCurrent(); child->doneCurrent();
} }
void GRenderWindow::PollEvents() { void GRenderWindow::PollEvents() {}
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
// //
// Older versions get the window size (density independent pixels), // Older versions get the window size (density independent pixels),
// and hence, do not support DPI scaling ("retina" displays). // and hence, do not support DPI scaling ("retina" displays).
// The result will be a viewport that is smaller than the extent of the window. // The result will be a viewport that is smaller than the extent of the window.
void GRenderWindow::OnFramebufferSizeChanged() void GRenderWindow::OnFramebufferSizeChanged() {
{ // Screen changes potentially incur a change in screen DPI, hence we should update the
// Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size // framebuffer size
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned width = child->QPaintDevice::width() * pixelRatio;
unsigned height = child->QPaintDevice::height() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio;
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
void GRenderWindow::BackupGeometry() void GRenderWindow::BackupGeometry() {
{
geometry = ((QGLWidget*)this)->saveGeometry(); geometry = ((QGLWidget*)this)->saveGeometry();
} }
void GRenderWindow::RestoreGeometry() void GRenderWindow::RestoreGeometry() {
{
// We don't want to back up the geometry here (obviously) // We don't want to back up the geometry here (obviously)
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
} }
void GRenderWindow::restoreGeometry(const QByteArray& geometry) void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
{
// Make sure users of this class don't need to deal with backing up the geometry themselves // Make sure users of this class don't need to deal with backing up the geometry themselves
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
BackupGeometry(); BackupGeometry();
} }
QByteArray GRenderWindow::saveGeometry() QByteArray GRenderWindow::saveGeometry() {
{
// If we are a top-level widget, store the current geometry // If we are a top-level widget, store the current geometry
// otherwise, store the last backup // otherwise, store the last backup
if (parent() == nullptr) if (parent() == nullptr)
@ -195,8 +190,7 @@ QByteArray GRenderWindow::saveGeometry()
return geometry; return geometry;
} }
qreal GRenderWindow::windowPixelRatio() qreal GRenderWindow::windowPixelRatio() {
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// windowHandle() might not be accessible until the window is displayed to screen. // windowHandle() might not be accessible until the window is displayed to screen.
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
@ -210,20 +204,16 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event); QWidget::closeEvent(event);
} }
void GRenderWindow::keyPressEvent(QKeyEvent* event) void GRenderWindow::keyPressEvent(QKeyEvent* event) {
{ KeyMap::PressKey(*this, {event->key(), keyboard_id});
KeyMap::PressKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
{ KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::mousePressEvent(QMouseEvent *event) void GRenderWindow::mousePressEvent(QMouseEvent* event) {
{ if (event->button() == Qt::LeftButton) {
if (event->button() == Qt::LeftButton)
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
@ -231,30 +221,28 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
} }
} }
void GRenderWindow::mouseMoveEvent(QMouseEvent *event) void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
} }
void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
{
if (event->button() == Qt::LeftButton) if (event->button() == Qt::LeftButton)
this->TouchReleased(); this->TouchReleased();
} }
void GRenderWindow::ReloadSetKeymaps() void GRenderWindow::ReloadSetKeymaps() {
{
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
{
NotifyClientAreaSizeChanged(std::make_pair(width, height)); NotifyClientAreaSizeChanged(std::make_pair(width, height));
} }
@ -267,7 +255,8 @@ void GRenderWindow::InitRenderTarget() {
delete layout(); delete layout();
} }
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QGLFormat fmt; QGLFormat fmt;
fmt.setVersion(3, 3); fmt.setVersion(3, 3);
fmt.setProfile(QGLFormat::CoreProfile); fmt.setProfile(QGLFormat::CoreProfile);
@ -279,7 +268,8 @@ void GRenderWindow::InitRenderTarget() {
child = new GGLWidgetInternal(fmt, this); child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this); QBoxLayout* layout = new QHBoxLayout(this);
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); resize(VideoCore::kScreenTopWidth,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
layout->addWidget(child); layout->addWidget(child);
layout->setMargin(0); layout->setMargin(0);
setLayout(layout); setLayout(layout);
@ -292,7 +282,8 @@ void GRenderWindow::InitRenderTarget() {
BackupGeometry(); BackupGeometry();
} }
void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { void GRenderWindow::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
setMinimumSize(minimal_size.first, minimal_size.second); setMinimumSize(minimal_size.first, minimal_size.second);
} }
@ -306,11 +297,12 @@ void GRenderWindow::OnEmulationStopping() {
child->EnablePainting(); child->EnablePainting();
} }
void GRenderWindow::showEvent(QShowEvent * event) { void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event); QWidget::showEvent(event);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// windowHandle() is not initialized until the Window is shown, so we connect it here. // windowHandle() is not initialized until the Window is shown, so we connect it here.
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this,
connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection); SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection);
#endif #endif
} }

View file

@ -5,10 +5,8 @@
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <QGLWidget> #include <QGLWidget>
#include <QThread> #include <QThread>
#include "common/emu_window.h" #include "common/emu_window.h"
#include "common/thread.h" #include "common/thread.h"
@ -19,8 +17,7 @@ class GGLWidgetInternal;
class GMainWindow; class GMainWindow;
class GRenderWindow; class GRenderWindow;
class EmuThread : public QThread class EmuThread : public QThread {
{
Q_OBJECT Q_OBJECT
public: public:
@ -58,7 +55,9 @@ public:
* @return True if the emulation thread is running, otherwise false * @return True if the emulation thread is running, otherwise false
* @note This function is thread-safe * @note This function is thread-safe
*/ */
bool IsRunning() { return running; } bool IsRunning() {
return running;
}
/** /**
* Requests for the emulation thread to stop running * Requests for the emulation thread to stop running
@ -81,20 +80,23 @@ signals:
/** /**
* Emitted when the CPU has halted execution * Emitted when the CPU has halted execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeEntered(); void DebugModeEntered();
/** /**
* Emitted right before the CPU continues execution * Emitted right before the CPU continues execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeLeft(); void DebugModeLeft();
}; };
class GRenderWindow : public QWidget, public EmuWindow class GRenderWindow : public QWidget, public EmuWindow {
{
Q_OBJECT Q_OBJECT
public: public:
@ -109,7 +111,7 @@ public:
void BackupGeometry(); void BackupGeometry();
void RestoreGeometry(); void RestoreGeometry();
void restoreGeometry(const QByteArray& geometry); // overridden void restoreGeometry(const QByteArray& geometry); // overridden
QByteArray saveGeometry(); // overridden QByteArray saveGeometry(); // overridden
qreal windowPixelRatio(); qreal windowPixelRatio();
@ -118,9 +120,9 @@ public:
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent* event) override;
void ReloadSetKeymaps() override; void ReloadSetKeymaps() override;
@ -129,7 +131,7 @@ public:
void InitRenderTarget(); void InitRenderTarget();
public slots: public slots:
void moveContext(); // overridden void moveContext(); // overridden
void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping(); void OnEmulationStopping();
@ -140,7 +142,8 @@ signals:
void Closed(); void Closed();
private: private:
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
GGLWidgetInternal* child; GGLWidgetInternal* child;

View file

@ -3,10 +3,8 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QSettings> #include <QSettings>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "common/file_util.h" #include "common/file_util.h"
Config::Config() { Config::Config() {
@ -20,24 +18,23 @@ Config::Config() {
const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
// directly mapped keys // directly mapped keys
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_K, Qt::Key_J, Qt::Key_L,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
// indirectly mapped keys // indirectly mapped keys
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
Qt::Key_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
qt_config->beginGroup("Controls"); qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
.toInt();
} }
Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); Settings::values.pad_circle_modifier_scale =
qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -48,17 +45,19 @@ void Config::ReadValues() {
qt_config->beginGroup("Renderer"); qt_config->beginGroup("Renderer");
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool();
Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool();
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.use_scaled_resolution =
qt_config->value("use_scaled_resolution", false).toBool();
Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat();
Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); Settings::values.enable_audio_stretching =
qt_config->value("enable_audio_stretching", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Data Storage"); qt_config->beginGroup("Data Storage");
@ -84,10 +83,14 @@ void Config::ReadValues() {
qt_config->beginGroup("UILayout"); qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray();
UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray(); UISettings::values.renderwindow_geometry =
UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray(); qt_config->value("geometryRenderWindow").toByteArray();
UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray(); UISettings::values.gamelist_header_state =
UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool(); qt_config->value("gameListHeaderState").toByteArray();
UISettings::values.microprofile_geometry =
qt_config->value("microProfileDialogGeometry").toByteArray();
UISettings::values.microprofile_visible =
qt_config->value("microProfileDialogVisible", false).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Paths"); qt_config->beginGroup("Paths");
@ -106,10 +109,10 @@ void Config::ReadValues() {
QStringList hotkeys = qt_config->childGroups(); QStringList hotkeys = qt_config->childGroups();
for (auto hotkey : hotkeys) { for (auto hotkey : hotkeys) {
qt_config->beginGroup(hotkey); qt_config->beginGroup(hotkey);
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
UISettings::Shortcut(group + "/" + hotkey, group + "/" + hotkey,
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
qt_config->value("Context").toInt()))); qt_config->value("Context").toInt())));
qt_config->endGroup(); qt_config->endGroup();
} }
@ -119,7 +122,7 @@ void Config::ReadValues() {
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool(); UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
@ -129,9 +132,10 @@ void Config::SaveValues() {
qt_config->beginGroup("Controls"); qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[i]]); Settings::values.input_mappings[Settings::NativeInput::All[i]]);
} }
qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); qt_config->setValue("pad_circle_modifier_scale",
(double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -146,9 +150,9 @@ void Config::SaveValues() {
qt_config->setValue("use_vsync", Settings::values.use_vsync); qt_config->setValue("use_vsync", Settings::values.use_vsync);
// Cast to double because Qt's written float values are not human-readable // Cast to double because Qt's written float values are not human-readable
qt_config->setValue("bg_red", (double)Settings::values.bg_red); qt_config->setValue("bg_red", (double)Settings::values.bg_red);
qt_config->setValue("bg_green", (double)Settings::values.bg_green); qt_config->setValue("bg_green", (double)Settings::values.bg_green);
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");

View file

@ -6,7 +6,6 @@
#include <string> #include <string>
#include <QVariant> #include <QVariant>
#include "core/settings.h" #include "core/settings.h"
class QSettings; class QSettings;
@ -17,6 +16,7 @@ class Config {
void ReadValues(); void ReadValues();
void SaveValues(); void SaveValues();
public: public:
Config(); Config();
~Config(); ~Config();

View file

@ -3,16 +3,12 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "audio_core/sink_details.h" #include "audio_core/sink_details.h"
#include "citra_qt/configure_audio.h" #include "citra_qt/configure_audio.h"
#include "core/settings.h"
#include "ui_configure_audio.h" #include "ui_configure_audio.h"
#include "core/settings.h" ConfigureAudio::ConfigureAudio(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ConfigureAudio::ConfigureAudio(QWidget* parent) :
QWidget(parent),
ui(std::make_unique<Ui::ConfigureAudio>())
{
ui->setupUi(this); ui->setupUi(this);
ui->output_sink_combo_box->clear(); ui->output_sink_combo_box->clear();
@ -24,8 +20,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) :
this->setConfiguration(); this->setConfiguration();
} }
ConfigureAudio::~ConfigureAudio() { ConfigureAudio::~ConfigureAudio() {}
}
void ConfigureAudio::setConfiguration() { void ConfigureAudio::setConfiguration() {
int new_sink_index = 0; int new_sink_index = 0;
@ -41,7 +36,9 @@ void ConfigureAudio::setConfiguration() {
} }
void ConfigureAudio::applyConfiguration() { void ConfigureAudio::applyConfiguration() {
Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::Apply(); Settings::Apply();
} }

View file

@ -3,20 +3,15 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "citra_qt/configure_debug.h" #include "citra_qt/configure_debug.h"
#include "core/settings.h"
#include "ui_configure_debug.h" #include "ui_configure_debug.h"
#include "core/settings.h" ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
ConfigureDebug::ConfigureDebug(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureDebug)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }
ConfigureDebug::~ConfigureDebug() { ConfigureDebug::~ConfigureDebug() {}
}
void ConfigureDebug::setConfiguration() { void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);

View file

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureDebug; class ConfigureDebug;
} }
class ConfigureDebug : public QWidget class ConfigureDebug : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDebug(QWidget *parent = nullptr); explicit ConfigureDebug(QWidget* parent = nullptr);
~ConfigureDebug(); ~ConfigureDebug();
void applyConfiguration(); void applyConfiguration();

View file

@ -4,24 +4,17 @@
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/configure_dialog.h" #include "citra_qt/configure_dialog.h"
#include "core/settings.h"
#include "ui_configure.h" #include "ui_configure.h"
ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
#include "core/settings.h"
ConfigureDialog::ConfigureDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ConfigureDialog)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }
ConfigureDialog::~ConfigureDialog() { ConfigureDialog::~ConfigureDialog() {}
}
void ConfigureDialog::setConfiguration() { void ConfigureDialog::setConfiguration() {}
}
void ConfigureDialog::applyConfiguration() { void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration(); ui->generalTab->applyConfiguration();

View file

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureDialog; class ConfigureDialog;
} }
class ConfigureDialog : public QDialog class ConfigureDialog : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDialog(QWidget *parent); explicit ConfigureDialog(QWidget* parent);
~ConfigureDialog(); ~ConfigureDialog();
void applyConfiguration(); void applyConfiguration();

View file

@ -4,23 +4,20 @@
#include "citra_qt/configure_general.h" #include "citra_qt/configure_general.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "ui_configure_general.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "ui_configure_general.h"
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureGeneral)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn()); ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn());
} }
ConfigureGeneral::~ConfigureGeneral() { ConfigureGeneral::~ConfigureGeneral() {}
}
void ConfigureGeneral::setConfiguration() { void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);

View file

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureGeneral; class ConfigureGeneral;
} }
class ConfigureGeneral : public QWidget class ConfigureGeneral : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGeneral(QWidget *parent = nullptr); explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral(); ~ConfigureGeneral();
void applyConfiguration(); void applyConfiguration();

View file

@ -3,23 +3,20 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "citra_qt/configure_graphics.h" #include "citra_qt/configure_graphics.h"
#include "ui_configure_graphics.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "ui_configure_graphics.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
ConfigureGraphics::ConfigureGraphics(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConfigureGraphics)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
ui->toggle_vsync->setEnabled(!System::IsPoweredOn()); ui->toggle_vsync->setEnabled(!System::IsPoweredOn());
} }
ConfigureGraphics::~ConfigureGraphics() { ConfigureGraphics::~ConfigureGraphics() {}
}
void ConfigureGraphics::setConfiguration() { void ConfigureGraphics::setConfiguration() {
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer);

View file

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureGraphics; class ConfigureGraphics;
} }
class ConfigureGraphics : public QWidget class ConfigureGraphics : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGraphics(QWidget *parent = nullptr); explicit ConfigureGraphics(QWidget* parent = nullptr);
~ConfigureGraphics(); ~ConfigureGraphics();
void applyConfiguration(); void applyConfiguration();

View file

@ -5,38 +5,39 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <QTimer> #include <QTimer>
#include "citra_qt/configure_input.h" #include "citra_qt/configure_input.h"
ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this); ui->setupUi(this);
// Initialize mapping of input enum to UI button. // Initialize mapping of input enum to UI button.
input_mapping = { input_mapping = {
{ std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) }, {Settings::NativeInput::Values::A, ui->buttonA},
{ std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) }, {Settings::NativeInput::Values::B, ui->buttonB},
{ std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) }, {Settings::NativeInput::Values::X, ui->buttonX},
{ std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) }, {Settings::NativeInput::Values::Y, ui->buttonY},
{ std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) }, {Settings::NativeInput::Values::L, ui->buttonL},
{ std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) }, {Settings::NativeInput::Values::R, ui->buttonR},
{ std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) }, {Settings::NativeInput::Values::ZL, ui->buttonZL},
{ std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) }, {Settings::NativeInput::Values::ZR, ui->buttonZR},
{ std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) }, {Settings::NativeInput::Values::START, ui->buttonStart},
{ std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) }, {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
{ std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) }, {Settings::NativeInput::Values::HOME, ui->buttonHome},
{ std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) }, {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
{ std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) }, {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
{ std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) }, {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
{ std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) }, {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
{ std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) }, {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
{ std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) }, {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
{ std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) }, {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
{ std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) }, {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) }, {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) }, {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) }, {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) }, {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) }, {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
}; };
// Attach handle click method to each button click. // Attach handle click method to each button click.
@ -47,7 +48,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
timer = new QTimer(this); timer = new QTimer(this);
timer->setSingleShot(true); timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); connect(timer, &QTimer::timeout, this, [&]() {
key_pressed = Qt::Key_Escape;
setKey();
});
this->setConfiguration(); this->setConfiguration();
} }
@ -59,7 +63,7 @@ void ConfigureInput::handleClick() {
grabKeyboard(); grabKeyboard();
grabMouse(); grabMouse();
changing_button = sender; changing_button = sender;
timer->start(5000); //Cancel after 5 seconds timer->start(5000); // Cancel after 5 seconds
} }
void ConfigureInput::applyConfiguration() { void ConfigureInput::applyConfiguration() {

View file

@ -4,9 +4,9 @@
#pragma once #pragma once
#include <QWidget> #include <memory>
#include <QKeyEvent> #include <QKeyEvent>
#include <QWidget>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "core/settings.h" #include "core/settings.h"
#include "ui_configure_input.h" #include "ui_configure_input.h"
@ -16,7 +16,7 @@ class QString;
class QTimer; class QTimer;
namespace Ui { namespace Ui {
class ConfigureInput; class ConfigureInput;
} }
class ConfigureInput : public QWidget { class ConfigureInput : public QWidget {
@ -39,7 +39,8 @@ private:
/// Load configuration settings into button text /// Load configuration settings into button text
void setConfiguration(); void setConfiguration();
/// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value. /// Check all inputs for duplicate keys. Clears out any other button with the same value as this
/// button's new value.
void removeDuplicates(const QString& newValue); void removeDuplicates(const QString& newValue);
/// Handle key press event for input tab when a button is 'waiting'. /// Handle key press event for input tab when a button is 'waiting'.

View file

@ -4,27 +4,24 @@
#include "citra_qt/configure_system.h" #include "citra_qt/configure_system.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/system.h"
#include "ui_configure_system.h" #include "ui_configure_system.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/system.h"
static const std::array<int, 12> days_in_month = {{ static const std::array<int, 12> days_in_month = {{
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
}}; }};
ConfigureSystem::ConfigureSystem(QWidget *parent) : ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
QWidget(parent),
ui(new Ui::ConfigureSystem) {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)),
SLOT(updateBirthdayComboBox(int)));
this->setConfiguration(); this->setConfiguration();
} }
ConfigureSystem::~ConfigureSystem() { ConfigureSystem::~ConfigureSystem() {}
}
void ConfigureSystem::setConfiguration() { void ConfigureSystem::setConfiguration() {
enabled = !System::IsPoweredOn(); enabled = !System::IsPoweredOn();
@ -54,13 +51,17 @@ void ConfigureSystem::setConfiguration() {
void ConfigureSystem::ReadSystemSettings() { void ConfigureSystem::ReadSystemSettings() {
// set username // set username
username = Service::CFG::GetUsername(); username = Service::CFG::GetUsername();
// ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 // TODO(wwylele): Use this when we move to Qt 5.5
ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); // ui->edit_username->setText(QString::fromStdU16String(username));
ui->edit_username->setText(
QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
// set birthday // set birthday
std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
updateBirthdayComboBox(birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable updateBirthdayComboBox(
birthmonth -
1); // explicitly update it because the signal from setCurrentIndex is not reliable
ui->combo_birthday->setCurrentIndex(birthday - 1); ui->combo_birthday->setCurrentIndex(birthday - 1);
// set system language // set system language
@ -79,8 +80,10 @@ void ConfigureSystem::applyConfiguration() {
bool modified = false; bool modified = false;
// apply username // apply username
// std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 // TODO(wwylele): Use this when we move to Qt 5.5
std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); // std::u16string new_username = ui->edit_username->text().toStdU16String();
std::u16string new_username(
reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
if (new_username != username) { if (new_username != username) {
Service::CFG::SetUsername(new_username); Service::CFG::SetUsername(new_username);
modified = true; modified = true;

View file

@ -11,12 +11,11 @@ namespace Ui {
class ConfigureSystem; class ConfigureSystem;
} }
class ConfigureSystem : public QWidget class ConfigureSystem : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureSystem(QWidget *parent = nullptr); explicit ConfigureSystem(QWidget* parent = nullptr);
~ConfigureSystem(); ~ConfigureSystem();
void applyConfiguration(); void applyConfiguration();

View file

@ -3,19 +3,15 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QStandardItemModel> #include <QStandardItemModel>
#include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/callstack.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) CallstackWidget::CallstackWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
callstack_model = new QStandardItemModel(this); callstack_model = new QStandardItemModel(this);
@ -27,29 +23,26 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
ui.treeView->setModel(callstack_model); ui.treeView->setModel(callstack_model);
} }
void CallstackWidget::OnDebugModeEntered() void CallstackWidget::OnDebugModeEntered() {
{
// Stack pointer // Stack pointer
const u32 sp = Core::g_app_core->GetReg(13); const u32 sp = Core::g_app_core->GetReg(13);
Clear(); Clear();
int counter = 0; int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4) for (u32 addr = 0x10000000; addr >= sp; addr -= 4) {
{
if (!Memory::IsValidVirtualAddress(addr)) if (!Memory::IsValidVirtualAddress(addr))
break; break;
const u32 ret_addr = Memory::Read32(addr); const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address??? const u32 call_addr = ret_addr - 4; // get call address???
if (!Memory::IsValidVirtualAddress(call_addr)) if (!Memory::IsValidVirtualAddress(call_addr))
break; break;
/* TODO (mattvail) clean me, move to debugger interface */ /* TODO (mattvail) clean me, move to debugger interface */
u32 insn = Memory::Read32(call_addr); u32 insn = Memory::Read32(call_addr);
if (ARM_Disasm::Decode(insn) == OP_BL) if (ARM_Disasm::Decode(insn) == OP_BL) {
{
std::string name; std::string name;
// ripped from disasm // ripped from disasm
u8 cond = (insn >> 28) & 0xf; u8 cond = (insn >> 28) & 0xf;
@ -63,26 +56,28 @@ void CallstackWidget::OnDebugModeEntered()
i_offset += 8; i_offset += 8;
const u32 func_addr = call_addr + i_offset; const u32 func_addr = call_addr + i_offset;
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(
ret_addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(
call_addr, 8, 16, QLatin1Char('0'))));
name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown";
callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) callstack_model->setItem(
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); counter, 3, new QStandardItem(
QString("%1_%2")
.arg(QString::fromStdString(name))
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0')))));
counter++; counter++;
} }
} }
} }
void CallstackWidget::OnDebugModeLeft() void CallstackWidget::OnDebugModeLeft() {}
{
} void CallstackWidget::Clear() {
void CallstackWidget::Clear()
{
for (int row = 0; row < callstack_model->rowCount(); row++) { for (int row = 0; row < callstack_model->rowCount(); row++) {
for (int column = 0; column < callstack_model->columnCount(); column++) { for (int column = 0; column < callstack_model->columnCount(); column++) {
callstack_model->setItem(row, column, new QStandardItem()); callstack_model->setItem(row, column, new QStandardItem());

View file

@ -7,8 +7,7 @@
class QStandardItemModel; class QStandardItemModel;
class CallstackWidget : public QDockWidget class CallstackWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -3,23 +3,20 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QShortcut> #include <QShortcut>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/break_points.h" #include "common/break_points.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
DisassemblerModel::DisassemblerModel(QObject* parent) : DisassemblerModel::DisassemblerModel(QObject* parent)
QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0),
} selection(QModelIndex()) {}
int DisassemblerModel::columnCount(const QModelIndex& parent) const { int DisassemblerModel::columnCount(const QModelIndex& parent) const {
return 3; return 3;
@ -31,62 +28,60 @@ int DisassemblerModel::rowCount(const QModelIndex& parent) const {
QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{ u32 address = base_address + index.row() * 4;
u32 address = base_address + index.row() * 4; u32 instr = Memory::Read32(address);
u32 instr = Memory::Read32(address); std::string disassembly = ARM_Disasm::Disassemble(address, instr);
std::string disassembly = ARM_Disasm::Disassemble(address, instr);
if (index.column() == 0) { if (index.column() == 0) {
return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0')); return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0'));
} else if (index.column() == 1) { } else if (index.column() == 1) {
return QString::fromStdString(disassembly); return QString::fromStdString(disassembly);
} else if (index.column() == 2) { } else if (index.column() == 2) {
if(Symbols::HasSymbol(address)) { if (Symbols::HasSymbol(address)) {
TSymbol symbol = Symbols::GetSymbol(address); TSymbol symbol = Symbols::GetSymbol(address);
return QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name)) return QString("%1 - Size:%2")
.arg(symbol.size / 4); // divide by 4 to get instruction count .arg(QString::fromStdString(symbol.name))
} else if (ARM_Disasm::Decode(instr) == OP_BL) { .arg(symbol.size / 4); // divide by 4 to get instruction count
u32 offset = instr & 0xFFFFFF; } else if (ARM_Disasm::Decode(instr) == OP_BL) {
u32 offset = instr & 0xFFFFFF;
// Sign-extend the 24-bit offset // Sign-extend the 24-bit offset
if ((offset >> 23) & 1) if ((offset >> 23) & 1)
offset |= 0xFF000000; offset |= 0xFF000000;
// Pre-compute the left-shift and the prefetch offset // Pre-compute the left-shift and the prefetch offset
offset <<= 2; offset <<= 2;
offset += 8; offset += 8;
TSymbol symbol = Symbols::GetSymbol(address + offset); TSymbol symbol = Symbols::GetSymbol(address + offset);
return QString(" --> %1").arg(QString::fromStdString(symbol.name)); return QString(" --> %1").arg(QString::fromStdString(symbol.name));
}
} }
break;
} }
case Qt::BackgroundRole: break;
{ }
unsigned int address = base_address + 4 * index.row();
if (breakpoints.IsAddressBreakPoint(address)) case Qt::BackgroundRole: {
return QBrush(QColor(0xFF, 0xC0, 0xC0)); unsigned int address = base_address + 4 * index.row();
else if (address == program_counter)
return QBrush(QColor(0xC0, 0xC0, 0xFF));
break; if (breakpoints.IsAddressBreakPoint(address))
return QBrush(QColor(0xFF, 0xC0, 0xC0));
else if (address == program_counter)
return QBrush(QColor(0xC0, 0xC0, 0xFF));
break;
}
case Qt::FontRole: {
if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
return GetMonospaceFont();
} }
break;
}
case Qt::FontRole: default:
{ break;
if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
return GetMonospaceFont();
}
break;
}
default:
break;
} }
return QVariant(); return QVariant();
@ -103,7 +98,7 @@ const BreakPoints& DisassemblerModel::GetBreakPoints() const {
void DisassemblerModel::ParseFromAddress(unsigned int address) { void DisassemblerModel::ParseFromAddress(unsigned int address) {
// NOTE: A too large value causes lagging when scrolling the disassembly // NOTE: A too large value causes lagging when scrolling the disassembly
const unsigned int chunk_size = 1000*500; const unsigned int chunk_size = 1000 * 500;
// If we haven't loaded anything yet, initialize base address to the parameter address // If we haven't loaded anything yet, initialize base address to the parameter address
if (code_size == 0) if (code_size == 0)
@ -165,23 +160,26 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
emit dataChanged(prev_index, prev_index); emit dataChanged(prev_index, prev_index);
} }
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) : DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread)
QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
disasm_ui.setupUi(this); disasm_ui.setupUi(this);
RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9),
Qt::ApplicationShortcut);
connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this,
SLOT(OnToggleStartStop()));
connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this,
SLOT(OnStepInto()));
setEnabled(false); setEnabled(false);
} }
@ -195,7 +193,8 @@ void DisassemblerWidget::Init() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC());
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnContinue() { void DisassemblerWidget::OnContinue() {
@ -234,11 +233,11 @@ void DisassemblerWidget::OnDebugModeEntered() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr);
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnDebugModeLeft() { void DisassemblerWidget::OnDebugModeLeft() {}
}
int DisassemblerWidget::SelectedRow() { int DisassemblerWidget::SelectedRow() {
QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
@ -254,10 +253,12 @@ void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) {
model = new DisassemblerModel(this); model = new DisassemblerModel(this);
disasm_ui.treeView->setModel(model); disasm_ui.treeView->setModel(model);
connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), connect(disasm_ui.treeView->selectionModel(),
model, SLOT(OnSelectionChanged(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model,
SLOT(OnSelectionChanged(const QModelIndex&)));
connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model,
SLOT(OnSetOrUnsetBreakpoint()));
Init(); Init();
setEnabled(true); setEnabled(true);

View file

@ -6,17 +6,14 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "ui_disassembler.h"
#include "common/break_points.h" #include "common/break_points.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "ui_disassembler.h"
class QAction; class QAction;
class EmuThread; class EmuThread;
class DisassemblerModel : public QAbstractListModel class DisassemblerModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -46,8 +43,7 @@ private:
mutable BreakPoints breakpoints; mutable BreakPoints breakpoints;
}; };
class DisassemblerWidget : public QDockWidget class DisassemblerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -3,75 +3,67 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QListView> #include <QListView>
#include "citra_qt/debugger/graphics.h" #include "citra_qt/debugger/graphics.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
extern GraphicsDebugger g_debugger; extern GraphicsDebugger g_debugger;
GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0) GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent)
{ : QAbstractListModel(parent), command_count(0) {
connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int)));
} }
int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const {
{
return command_count; return command_count;
} }
QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const {
{
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
int command_index = index.row(); int command_index = index.row();
const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index);
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole) {
{
std::map<GSP_GPU::CommandId, const char*> command_names = { std::map<GSP_GPU::CommandId, const char*> command_names = {
{ GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA" }, {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"},
{ GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST" }, {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"},
{ GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" }, {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"},
{ GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" }, {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"},
{ GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" }, {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"},
{ GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH" }, {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"},
}; };
const u32* command_data = reinterpret_cast<const u32*>(&command); const u32* command_data = reinterpret_cast<const u32*>(&command);
QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[command.id]) QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9")
.arg(command_data[0], 8, 16, QLatin1Char('0')) .arg(command_names[command.id])
.arg(command_data[1], 8, 16, QLatin1Char('0')) .arg(command_data[0], 8, 16, QLatin1Char('0'))
.arg(command_data[2], 8, 16, QLatin1Char('0')) .arg(command_data[1], 8, 16, QLatin1Char('0'))
.arg(command_data[3], 8, 16, QLatin1Char('0')) .arg(command_data[2], 8, 16, QLatin1Char('0'))
.arg(command_data[4], 8, 16, QLatin1Char('0')) .arg(command_data[3], 8, 16, QLatin1Char('0'))
.arg(command_data[5], 8, 16, QLatin1Char('0')) .arg(command_data[4], 8, 16, QLatin1Char('0'))
.arg(command_data[6], 8, 16, QLatin1Char('0')) .arg(command_data[5], 8, 16, QLatin1Char('0'))
.arg(command_data[7], 8, 16, QLatin1Char('0')); .arg(command_data[6], 8, 16, QLatin1Char('0'))
.arg(command_data[7], 8, 16, QLatin1Char('0'));
return QVariant(str); return QVariant(str);
} } else {
else
{
return QVariant(); return QVariant();
} }
} }
void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) {
{
emit GXCommandFinished(total_command_count); emit GXCommandFinished(total_command_count);
} }
void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) {
{
if (total_command_count == 0) if (total_command_count == 0)
return; return;
int prev_command_count = command_count; int prev_command_count = command_count;
command_count = total_command_count; command_count = total_command_count;
emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0)); emit dataChanged(index(prev_command_count, 0), index(total_command_count - 1, 0));
} }
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent) : QDockWidget(tr("Graphics Debugger"), parent) {
{
setObjectName("GraphicsDebugger"); setObjectName("GraphicsDebugger");
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);

View file

@ -6,11 +6,10 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/gpu_debugger.h" #include "video_core/gpu_debugger.h"
class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver class GPUCommandStreamItemModel : public QAbstractListModel,
{ public GraphicsDebugger::DebuggerObserver {
Q_OBJECT Q_OBJECT
public: public:
@ -32,8 +31,7 @@ private:
int command_count; int command_count;
}; };
class GPUCommandStreamWidget : public QDockWidget class GPUCommandStreamWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -3,30 +3,25 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QMetaType> #include <QMetaType>
#include "citra_qt/debugger/graphics_breakpoint_observer.h" #include "citra_qt/debugger/graphics_breakpoint_observer.h"
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
const QString& title, QWidget* parent) const QString& title, QWidget* parent)
: QDockWidget(title, parent), BreakPointObserver(debug_context) : QDockWidget(title, parent), BreakPointObserver(debug_context) {
{
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
// NOTE: This signal is emitted from a non-GUI thread, but connect() takes // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
// care of delaying its handling to the GUI thread. // care of delaying its handling to the GUI thread.
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
} }
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void BreakPointObserverDock::OnPicaResume() void BreakPointObserverDock::OnPicaResume() {
{
emit Resumed(); emit Resumed();
} }

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
/** /**
@ -13,7 +12,8 @@
* This is because the Pica breakpoint callbacks are called from a non-GUI thread, while * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
* the widget usually wants to perform reactions in the GUI thread. * the widget usually wants to perform reactions in the GUI thread.
*/ */
class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { class BreakPointObserverDock : public QDockWidget,
protected Pica::DebugContext::BreakPointObserver {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -7,47 +7,39 @@
#include <QPushButton> #include <QPushButton>
#include <QTreeView> #include <QTreeView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "citra_qt/debugger/graphics_breakpoints.h" #include "citra_qt/debugger/graphics_breakpoints.h"
#include "citra_qt/debugger/graphics_breakpoints_p.h" #include "citra_qt/debugger/graphics_breakpoints_p.h"
#include "common/assert.h" #include "common/assert.h"
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
: QAbstractListModel(parent), context_weak(debug_context), : QAbstractListModel(parent), context_weak(debug_context),
at_breakpoint(debug_context->at_breakpoint), at_breakpoint(debug_context->at_breakpoint),
active_breakpoint(debug_context->active_breakpoint) active_breakpoint(debug_context->active_breakpoint) {}
{
} int BreakPointModel::columnCount(const QModelIndex& parent) const {
int BreakPointModel::columnCount(const QModelIndex& parent) const
{
return 1; return 1;
} }
int BreakPointModel::rowCount(const QModelIndex& parent) const int BreakPointModel::rowCount(const QModelIndex& parent) const {
{
return static_cast<int>(Pica::DebugContext::Event::NumEvents); return static_cast<int>(Pica::DebugContext::Event::NumEvents);
} }
QVariant BreakPointModel::data(const QModelIndex& index, int role) const QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
if (index.column() == 0) { if (index.column() == 0) {
static const std::map<Pica::DebugContext::Event, QString> map = { static const std::map<Pica::DebugContext::Event, QString> map = {
{ Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
{ Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
{ Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") }, {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
{ Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, {Pica::DebugContext::Event::IncomingDisplayTransfer,
{ Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, tr("Incoming display transfer")},
{ Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
{Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")},
}; };
DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
@ -57,23 +49,20 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
break; break;
} }
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() == 0) if (index.column() == 0)
return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
break; break;
} }
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
return QBrush(QColor(0xE0, 0xE0, 0x10)); return QBrush(QColor(0xE0, 0xE0, 0x10));
} }
break; break;
} }
case Role_IsEnabled: case Role_IsEnabled: {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
return context && context->breakpoints[(int)event].enabled; return context && context->breakpoints[(int)event].enabled;
} }
@ -84,8 +73,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
{
if (!index.isValid()) if (!index.isValid())
return 0; return 0;
@ -95,14 +83,11 @@ Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
return flags; return flags;
} }
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() != 0) if (index.column() != 0)
return false; return false;
@ -120,9 +105,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i
return false; return false;
} }
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) {
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -133,8 +116,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
createIndex(static_cast<int>(event), 0)); createIndex(static_cast<int>(event), 0));
} }
void BreakPointModel::OnResumed() void BreakPointModel::OnResumed() {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -145,12 +127,10 @@ void BreakPointModel::OnResumed()
active_breakpoint = context->active_breakpoint; active_breakpoint = context->active_breakpoint;
} }
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
QWidget* parent)
: QDockWidget(tr("Pica Breakpoints"), parent), : QDockWidget(tr("Pica Breakpoints"), parent),
Pica::DebugContext::BreakPointObserver(debug_context) Pica::DebugContext::BreakPointObserver(debug_context) {
{
setObjectName("PicaBreakPointsWidget"); setObjectName("PicaBreakPointsWidget");
status_text = new QLabel(tr("Emulation running")); status_text = new QLabel(tr("Emulation running"));
@ -165,23 +145,21 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
this, SLOT(OnItemDoubleClicked(const QModelIndex&))); SLOT(OnItemDoubleClicked(const QModelIndex&)));
connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model,
breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
QWidget* main_widget = new QWidget; QWidget* main_widget = new QWidget;
auto main_layout = new QVBoxLayout; auto main_layout = new QVBoxLayout;
@ -197,38 +175,32 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
setWidget(main_widget); setWidget(main_widget);
} }
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) {
{
// Process in GUI thread // Process in GUI thread
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
status_text->setText(tr("Emulation halted at breakpoint")); status_text->setText(tr("Emulation halted at breakpoint"));
resume_button->setEnabled(true); resume_button->setEnabled(true);
} }
void GraphicsBreakPointsWidget::OnPicaResume() void GraphicsBreakPointsWidget::OnPicaResume() {
{
// Process in GUI thread // Process in GUI thread
emit Resumed(); emit Resumed();
} }
void GraphicsBreakPointsWidget::OnResumed() void GraphicsBreakPointsWidget::OnResumed() {
{
status_text->setText(tr("Emulation running")); status_text->setText(tr("Emulation running"));
resume_button->setEnabled(false); resume_button->setEnabled(false);
} }
void GraphicsBreakPointsWidget::OnResumeRequested() void GraphicsBreakPointsWidget::OnResumeRequested() {
{
if (auto context = context_weak.lock()) if (auto context = context_weak.lock())
context->Resume(); context->Resume();
} }
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
{
if (!index.isValid()) if (!index.isValid())
return; return;

View file

@ -5,9 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
class QLabel; class QLabel;

View file

@ -5,9 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QAbstractListModel> #include <QAbstractListModel>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
class BreakPointModel : public QAbstractListModel { class BreakPointModel : public QAbstractListModel {
@ -23,7 +21,7 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

View file

@ -13,16 +13,13 @@
#include <QSpinBox> #include <QSpinBox>
#include <QTreeView> #include <QTreeView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "citra_qt/debugger/graphics_cmdlists.h" #include "citra_qt/debugger/graphics_cmdlists.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/debug_utils/debug_utils.h"
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
@ -38,7 +35,8 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
class TextureInfoWidget : public QWidget { class TextureInfoWidget : public QWidget {
public: public:
TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr)
: QWidget(parent) {
QLabel* image_widget = new QLabel; QLabel* image_widget = new QLabel;
QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
@ -50,9 +48,7 @@ public:
} }
}; };
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {}
}
int GPUCommandListModel::rowCount(const QModelIndex& parent) const { int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
return static_cast<int>(pica_trace.writes.size()); return static_cast<int>(pica_trace.writes.size());
@ -70,7 +66,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
QString content; QString content;
switch ( index.column() ) { switch (index.column()) {
case 0: case 0:
return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
case 1: case 1:
@ -88,9 +84,8 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
} }
QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
switch(role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (section) { switch (section) {
case 0: case 0:
return tr("Command Name"); return tr("Command Name");
@ -117,14 +112,14 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
endResetModel(); endResetModel();
} }
#define COMMAND_IN_RANGE(cmd_id, reg_name) \ #define COMMAND_IN_RANGE(cmd_id, reg_name) \
(cmd_id >= PICA_REG_INDEX(reg_name) && \ (cmd_id >= PICA_REG_INDEX(reg_name) && \
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4) cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -148,9 +143,9 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
QWidget* new_info_widget = nullptr; QWidget* new_info_widget = nullptr;
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -179,7 +174,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
} }
#undef COMMAND_IN_RANGE #undef COMMAND_IN_RANGE
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
: QDockWidget(tr("Pica Command List"), parent) {
setObjectName("Pica Command List"); setObjectName("Pica Command List");
GPUCommandListModel* model = new GPUCommandListModel(this); GPUCommandListModel* model = new GPUCommandListModel(this);
@ -191,23 +187,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
list_widget->setRootIsDecorated(false); list_widget->setRootIsDecorated(false);
list_widget->setUniformRowHeights(true); list_widget->setUniformRowHeights(true);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
#else #else
list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setResizeMode(QHeaderView::ResizeToContents);
#endif #endif
connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), connect(list_widget->selectionModel(),
this, SLOT(SetCommandInfo(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this,
connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(SetCommandInfo(const QModelIndex&)));
this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this,
SLOT(OnCommandDoubleClicked(const QModelIndex&)));
toggle_tracing = new QPushButton(tr("Start Tracing")); toggle_tracing = new QPushButton(tr("Start Tracing"));
QPushButton* copy_all = new QPushButton(tr("Copy All")); QPushButton* copy_all = new QPushButton(tr("Copy All"));
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model,
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));

View file

@ -6,15 +6,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/gpu_debugger.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_debugger.h"
class QPushButton; class QPushButton;
class QTreeView; class QTreeView;
class GPUCommandListModel : public QAbstractListModel class GPUCommandListModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -27,7 +25,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
public slots: public slots:
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
@ -36,8 +35,7 @@ private:
Pica::DebugUtils::PicaTrace pica_trace; Pica::DebugUtils::PicaTrace pica_trace;
}; };
class GPUCommandListWidget : public QDockWidget class GPUCommandListWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -11,24 +11,20 @@
#include <QPushButton> #include <QPushButton>
#include <QScrollArea> #include <QScrollArea>
#include <QSpinBox> #include <QSpinBox>
#include "citra_qt/debugger/graphics_surface.h" #include "citra_qt/debugger/graphics_surface.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/color.h" #include "common/color.h"
#include "core/memory.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/utils.h" #include "video_core/utils.h"
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
SurfacePicture::~SurfacePicture() {} SurfacePicture::~SurfacePicture() {}
void SurfacePicture::mousePressEvent(QMouseEvent* event) void SurfacePicture::mousePressEvent(QMouseEvent* event) {
{
// Only do something while the left mouse button is held down // Only do something while the left mouse button is held down
if (!(event->buttons() & Qt::LeftButton)) if (!(event->buttons() & Qt::LeftButton))
return; return;
@ -41,18 +37,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event)
event->y() * pixmap()->height() / height()); event->y() * pixmap()->height() / height());
} }
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
{
// We also want to handle the event if the user moves the mouse while holding down the LMB // We also want to handle the event if the user moves the mouse while holding down the LMB
mousePressEvent(event); mousePressEvent(event);
} }
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent) QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
surface_source(Source::ColorBuffer) surface_source(Source::ColorBuffer) {
{
setObjectName("PicaSurface"); setObjectName("PicaSurface");
surface_source_list = new QComboBox; surface_source_list = new QComboBox;
@ -124,13 +117,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
// Connections // Connections
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int))); connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64))); SLOT(OnSurfaceSourceChanged(int)));
connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int))); connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int))); SLOT(OnSurfaceAddressChanged(qint64)));
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int))); connect(surface_width_control, SIGNAL(valueChanged(int)), this,
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int))); SLOT(OnSurfaceWidthChanged(int)));
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int))); connect(surface_height_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfaceHeightChanged(int)));
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
SLOT(OnSurfaceFormatChanged(int)));
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfacePickerXChanged(int)));
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfacePickerYChanged(int)));
connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
auto main_widget = new QWidget; auto main_widget = new QWidget;
@ -203,25 +203,21 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
} }
} }
void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit Update(); emit Update();
widget()->setEnabled(true); widget()->setEnabled(true);
} }
void GraphicsSurfaceWidget::OnResumed() void GraphicsSurfaceWidget::OnResumed() {
{
widget()->setEnabled(false); widget()->setEnabled(false);
} }
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
{
surface_source = static_cast<Source>(new_value); surface_source = static_cast<Source>(new_value);
emit Update(); emit Update();
} }
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
{
if (surface_address != new_value) { if (surface_address != new_value) {
surface_address = static_cast<unsigned>(new_value); surface_address = static_cast<unsigned>(new_value);
@ -230,8 +226,7 @@ void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
{
if (surface_width != static_cast<unsigned>(new_value)) { if (surface_width != static_cast<unsigned>(new_value)) {
surface_width = static_cast<unsigned>(new_value); surface_width = static_cast<unsigned>(new_value);
@ -240,8 +235,7 @@ void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
{
if (surface_height != static_cast<unsigned>(new_value)) { if (surface_height != static_cast<unsigned>(new_value)) {
surface_height = static_cast<unsigned>(new_value); surface_height = static_cast<unsigned>(new_value);
@ -250,8 +244,7 @@ void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
{
if (surface_format != static_cast<Format>(new_value)) { if (surface_format != static_cast<Format>(new_value)) {
surface_format = static_cast<Format>(new_value); surface_format = static_cast<Format>(new_value);
@ -260,24 +253,21 @@ void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
{
if (surface_picker_x != new_value) { if (surface_picker_x != new_value) {
surface_picker_x = new_value; surface_picker_x = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
{
if (surface_picker_y != new_value) { if (surface_picker_y != new_value) {
surface_picker_y = new_value; surface_picker_y = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::Pick(int x, int y) void GraphicsSurfaceWidget::Pick(int x, int y) {
{
surface_picker_x_control->setValue(x); surface_picker_x_control->setValue(x);
surface_picker_y_control->setValue(y); surface_picker_y_control->setValue(y);
@ -312,74 +302,63 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
auto GetText = [offset](Format format, const u8* pixel) { auto GetText = [offset](Format format, const u8* pixel) {
switch (format) { switch (format) {
case Format::RGBA8: case Format::RGBA8: {
{
auto value = Color::DecodeRGBA8(pixel) / 255.0f; auto value = Color::DecodeRGBA8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB8: case Format::RGB8: {
{
auto value = Color::DecodeRGB8(pixel) / 255.0f; auto value = Color::DecodeRGB8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGB5A1: case Format::RGB5A1: {
{
auto value = Color::DecodeRGB5A1(pixel) / 255.0f; auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB565: case Format::RGB565: {
{
auto value = Color::DecodeRGB565(pixel) / 255.0f; auto value = Color::DecodeRGB565(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGBA4: case Format::RGBA4: {
{
auto value = Color::DecodeRGBA4(pixel) / 255.0f; auto value = Color::DecodeRGBA4(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::IA8: case Format::IA8:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
.arg(pixel[0])
.arg(pixel[1]);
case Format::RG8: { case Format::RG8: {
auto value = Color::DecodeRG8(pixel) / 255.0f; auto value = Color::DecodeRG8(pixel) / 255.0f;
return QString("Red: %1, Green: %2") return QString("Red: %1, Green: %2")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)); .arg(QString::number(value.g(), 'f', 2));
} }
case Format::I8: case Format::I8:
return QString("Index: %1").arg(*pixel); return QString("Index: %1").arg(*pixel);
case Format::A8: case Format::A8:
return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
case Format::IA4: case Format::IA4:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
.arg(*pixel & 0xF) case Format::I4: {
.arg((*pixel & 0xF0) >> 4);
case Format::I4:
{
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Index: %1").arg(i); return QString("Index: %1").arg(i);
} }
case Format::A4: case Format::A4: {
{
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
} }
@ -387,21 +366,20 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
case Format::ETC1A4: case Format::ETC1A4:
// TODO: Display block information or channel values? // TODO: Display block information or channel values?
return QString("Compressed data"); return QString("Compressed data");
case Format::D16: case Format::D16: {
{
auto value = Color::DecodeD16(pixel); auto value = Color::DecodeD16(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
} }
case Format::D24: case Format::D24: {
{
auto value = Color::DecodeD24(pixel); auto value = Color::DecodeD24(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
} }
case Format::D24X8: case Format::D24X8:
case Format::X24S8: case Format::X24S8: {
{
auto values = Color::DecodeD24S8(pixel); auto values = Color::DecodeD24S8(pixel);
return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]); return QString("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
.arg(values[1]);
} }
case Format::Unknown: case Format::Unknown:
return QString("Unknown format"); return QString("Unknown format");
@ -422,18 +400,18 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
nibbles.append(QString::number(nibble, 16).toUpper()); nibbles.append(QString::number(nibble, 16).toUpper());
} }
surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel))); surface_info_label->setText(
QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
} }
void GraphicsSurfaceWidget::OnUpdate() void GraphicsSurfaceWidget::OnUpdate() {
{
QPixmap pixmap; QPixmap pixmap;
switch (surface_source) { switch (surface_source) {
case Source::ColorBuffer: case Source::ColorBuffer: {
{ // TODO: Store a reference to the registers in the debug context instead of accessing them
// TODO: Store a reference to the registers in the debug context instead of accessing them directly... // directly...
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
@ -470,8 +448,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::DepthBuffer: case Source::DepthBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -499,8 +476,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::StencilBuffer: case Source::StencilBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -522,12 +498,14 @@ void GraphicsSurfaceWidget::OnUpdate()
case Source::Texture0: case Source::Texture0:
case Source::Texture1: case Source::Texture1:
case Source::Texture2: case Source::Texture2: {
{
unsigned texture_index; unsigned texture_index;
if (surface_source == Source::Texture0) texture_index = 0; if (surface_source == Source::Texture0)
else if (surface_source == Source::Texture1) texture_index = 1; texture_index = 0;
else if (surface_source == Source::Texture2) texture_index = 2; else if (surface_source == Source::Texture1)
texture_index = 1;
else if (surface_source == Source::Texture2)
texture_index = 2;
else { else {
qDebug() << "Unknown texture source " << static_cast<int>(surface_source); qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
break; break;
@ -547,8 +525,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::Custom: case Source::Custom: {
{
// Keep user-specified values // Keep user-specified values
break; break;
} }
@ -613,7 +590,8 @@ void GraphicsSurfaceWidget::OnUpdate()
} else { } else {
ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel"); ASSERT_MSG(nibbles_per_pixel >= 2,
"Depth decoder only supports formats with at least one byte per pixel");
unsigned bytes_per_pixel = nibbles_per_pixel / 2; unsigned bytes_per_pixel = nibbles_per_pixel / 2;
for (unsigned int y = 0; y < surface_height; ++y) { for (unsigned int y = 0; y < surface_height; ++y) {
@ -621,34 +599,30 @@ void GraphicsSurfaceWidget::OnUpdate()
const u32 coarse_y = y & ~7; const u32 coarse_y = y & ~7;
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
const u8* pixel = buffer + offset; const u8* pixel = buffer + offset;
Math::Vec4<u8> color = { 0, 0, 0, 0 }; Math::Vec4<u8> color = {0, 0, 0, 0};
switch(surface_format) { switch (surface_format) {
case Format::D16: case Format::D16: {
{
u32 data = Color::DecodeD16(pixel); u32 data = Color::DecodeD16(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
break; break;
} }
case Format::D24: case Format::D24: {
{
u32 data = Color::DecodeD24(pixel); u32 data = Color::DecodeD24(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
color.b() = (data >> 16) & 0xFF; color.b() = (data >> 16) & 0xFF;
break; break;
} }
case Format::D24X8: case Format::D24X8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = data.x & 0xFF; color.r() = data.x & 0xFF;
color.g() = (data.x >> 8) & 0xFF; color.g() = (data.x >> 8) & 0xFF;
color.b() = (data.x >> 16) & 0xFF; color.b() = (data.x >> 16) & 0xFF;
break; break;
} }
case Format::X24S8: case Format::X24S8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = color.g() = color.b() = data.y; color.r() = color.g() = color.b() = data.y;
break; break;
@ -661,7 +635,6 @@ void GraphicsSurfaceWidget::OnUpdate()
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255)); decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
} }
} }
} }
pixmap = QPixmap::fromImage(decoded_image); pixmap = QPixmap::fromImage(decoded_image);
@ -682,8 +655,10 @@ void GraphicsSurfaceWidget::SaveSurface() {
QString bin_filter = tr("Binary data (*.bin)"); QString bin_filter = tr("Binary data (*.bin)");
QString selectedFilter; QString selectedFilter;
QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), QString filename = QFileDialog::getSaveFileName(
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); this, tr("Save Surface"),
QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
if (filename.isEmpty()) { if (filename.isEmpty()) {
// If the user canceled the dialog, don't save anything. // If the user canceled the dialog, don't save anything.
@ -718,19 +693,18 @@ unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Forma
} }
switch (format) { switch (format) {
case Format::D24X8: case Format::D24X8:
case Format::X24S8: case Format::X24S8:
return 4 * 2; return 4 * 2;
case Format::D24: case Format::D24:
return 3 * 2; return 3 * 2;
case Format::D16: case Format::D16:
return 2 * 2; return 2 * 2;
default: default:
UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this " UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this "
"should not be reached as this function should " "function should be given a format which is in "
"be given a format which is in " "GraphicsSurfaceWidget::Format. Instead got %i",
"GraphicsSurfaceWidget::Format. Instead got %i", static_cast<int>(format));
static_cast<int>(format)); return 0;
return 0;
} }
} }

View file

@ -4,10 +4,9 @@
#pragma once #pragma once
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
class QComboBox; class QComboBox;
class QSpinBox; class QSpinBox;
@ -15,8 +14,7 @@ class CSpinBox;
class GraphicsSurfaceWidget; class GraphicsSurfaceWidget;
class SurfacePicture : public QLabel class SurfacePicture : public QLabel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -29,7 +27,6 @@ protected slots:
private: private:
GraphicsSurfaceWidget* surface_widget; GraphicsSurfaceWidget* surface_widget;
}; };
class GraphicsSurfaceWidget : public BreakPointObserverDock { class GraphicsSurfaceWidget : public BreakPointObserverDock {
@ -38,43 +35,44 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
using Event = Pica::DebugContext::Event; using Event = Pica::DebugContext::Event;
enum class Source { enum class Source {
ColorBuffer = 0, ColorBuffer = 0,
DepthBuffer = 1, DepthBuffer = 1,
StencilBuffer = 2, StencilBuffer = 2,
Texture0 = 3, Texture0 = 3,
Texture1 = 4, Texture1 = 4,
Texture2 = 5, Texture2 = 5,
Custom = 6, Custom = 6,
}; };
enum class Format { enum class Format {
// These must match the TextureFormat type! // These must match the TextureFormat type!
RGBA8 = 0, RGBA8 = 0,
RGB8 = 1, RGB8 = 1,
RGB5A1 = 2, RGB5A1 = 2,
RGB565 = 3, RGB565 = 3,
RGBA4 = 4, RGBA4 = 4,
IA8 = 5, IA8 = 5,
RG8 = 6, ///< @note Also called HILO8 in 3DBrew. RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
I8 = 7, I8 = 7,
A8 = 8, A8 = 8,
IA4 = 9, IA4 = 9,
I4 = 10, I4 = 10,
A4 = 11, A4 = 11,
ETC1 = 12, // compressed ETC1 = 12, // compressed
ETC1A4 = 13, ETC1A4 = 13,
MaxTextureFormat = 13, MaxTextureFormat = 13,
D16 = 14, D16 = 14,
D24 = 15, D24 = 15,
D24X8 = 16, D24X8 = 16,
X24S8 = 17, X24S8 = 17,
Unknown = 18, Unknown = 18,
}; };
static unsigned int NibblesPerPixel(Format format); static unsigned int NibblesPerPixel(Format format);
public: public:
GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
void Pick(int x, int y); void Pick(int x, int y);
public slots: public slots:
@ -97,7 +95,6 @@ signals:
void Update(); void Update();
private: private:
QComboBox* surface_source_list; QComboBox* surface_source_list;
CSpinBox* surface_address_control; CSpinBox* surface_address_control;
QSpinBox* surface_width_control; QSpinBox* surface_width_control;

View file

@ -6,25 +6,18 @@
#include <array> #include <array>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <boost/range/algorithm/copy.hpp>
#include <QBoxLayout> #include <QBoxLayout>
#include <QComboBox> #include <QComboBox>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <boost/range/algorithm/copy.hpp>
#include "citra_qt/debugger/graphics_tracing.h" #include "citra_qt/debugger/graphics_tracing.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "core/hw/lcd.h" #include "core/hw/lcd.h"
#include "core/tracer/recorder.h" #include "core/tracer/recorder.h"
#include "nihstro/float24.h" #include "nihstro/float24.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
@ -35,12 +28,16 @@ GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext>
setObjectName("CiTracing"); setObjectName("CiTracing");
QPushButton* start_recording = new QPushButton(tr("Start Recording")); QPushButton* start_recording = new QPushButton(tr("Start Recording"));
QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); QPushButton* stop_recording =
new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording,
connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording,
SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording,
SLOT(setVisible(bool)));
connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
@ -74,26 +71,31 @@ void GraphicsTracingWidget::StartRecording() {
std::array<u32, 4 * 16> default_attributes; std::array<u32, 4 * 16> default_attributes;
for (unsigned i = 0; i < 16; ++i) { for (unsigned i = 0; i < 16; ++i) {
for (unsigned comp = 0; comp < 3; ++comp) { for (unsigned comp = 0; comp < 3; ++comp) {
default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32()); default_attributes[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32());
} }
} }
std::array<u32, 4 * 96> vs_float_uniforms; std::array<u32, 4 * 96> vs_float_uniforms;
for (unsigned i = 0; i < 96; ++i) for (unsigned i = 0; i < 96; ++i)
for (unsigned comp = 0; comp < 3; ++comp) for (unsigned comp = 0; comp < 3; ++comp)
vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); vs_float_uniforms[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
CiTrace::Recorder::InitialState state; CiTrace::Recorder::InitialState state;
std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); std::back_inserter(state.gpu_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32),
std::back_inserter(state.lcd_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32),
std::back_inserter(state.pica_registers));
boost::copy(default_attributes, std::back_inserter(state.default_attributes)); boost::copy(default_attributes, std::back_inserter(state.default_attributes));
boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
auto recorder = new CiTrace::Recorder(state); auto recorder = new CiTrace::Recorder(state);
context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
@ -156,11 +158,12 @@ void GraphicsTracingWidget::OnEmulationStopping() {
if (!context) if (!context)
return; return;
if (context->recorder) { if (context->recorder) {
auto reply = QMessageBox::question(this, tr("CiTracing still active"), auto reply =
tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), QMessageBox::question(this, tr("CiTracing still active"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); tr("A CiTrace is still being recorded. Do you want to save it? "
"If not, all recorded data will be discarded."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) { if (reply == QMessageBox::Yes) {
StopRecording(); StopRecording();

View file

@ -12,7 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock {
Q_OBJECT Q_OBJECT
public: public:
GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
private slots: private slots:
void StartRecording(); void StartRecording();

View file

@ -4,7 +4,6 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <QBoxLayout> #include <QBoxLayout>
#include <QFileDialog> #include <QFileDialog>
#include <QFormLayout> #include <QFormLayout>
@ -15,10 +14,8 @@
#include <QSignalMapper> #include <QSignalMapper>
#include <QSpinBox> #include <QSpinBox>
#include <QTreeView> #include <QTreeView>
#include "citra_qt/debugger/graphics_vertex_shader.h" #include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/shader/shader.h" #include "video_core/shader/shader.h"
@ -28,9 +25,8 @@ using nihstro::Instruction;
using nihstro::SourceRegister; using nihstro::SourceRegister;
using nihstro::SwizzlePattern; using nihstro::SwizzlePattern;
GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) { GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent)
: QAbstractTableModel(parent), par(parent) {}
}
int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
return 3; return 3;
@ -40,10 +36,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
return static_cast<int>(par->info.code.size()); return static_cast<int>(par->info.code.size());
} }
QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation,
switch(role) { int role) const {
case Qt::DisplayRole: switch (role) {
{ case Qt::DisplayRole: {
if (section == 0) { if (section == 0) {
return tr("Offset"); return tr("Offset");
} else if (section == 1) { } else if (section == 1) {
@ -69,8 +65,8 @@ static std::string SelectorToString(u32 selector) {
} }
// e.g. "-c92[a0.x].xyzw" // e.g. "-c92[a0.x].xyzw"
static void print_input(std::ostringstream& output, const SourceRegister& input, static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate,
bool negate, const std::string& swizzle_mask, bool align = true, const std::string& swizzle_mask, bool align = true,
const std::string& address_register_name = std::string()) { const std::string& address_register_name = std::string()) {
if (align) if (align)
output << std::setw(4) << std::right; output << std::setw(4) << std::right;
@ -83,20 +79,18 @@ static void print_input(std::ostringstream& output, const SourceRegister& input,
QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (index.column()) { switch (index.column()) {
case 0: case 0:
if (par->info.HasLabel(index.row())) if (par->info.HasLabel(index.row()))
return QString::fromStdString(par->info.GetLabel(index.row())); return QString::fromStdString(par->info.GetLabel(index.row()));
return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0'));
case 1: case 1:
return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
case 2: case 2: {
{
std::ostringstream output; std::ostringstream output;
output.flags(std::ios::uppercase); output.flags(std::ios::uppercase);
@ -117,8 +111,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
const Instruction instr = par->info.code[index.row()]; const Instruction instr = par->info.code[index.row()];
const OpCode opcode = instr.opcode; const OpCode opcode = instr.opcode;
const OpCode::Info opcode_info = opcode.GetInfo(); const OpCode::Info opcode_info = opcode.GetInfo();
const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ? const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value(); ? instr.mad.operand_desc_id.Value()
: instr.common.operand_desc_id.Value();
const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
// longest known instruction name: "setemit " // longest known instruction name: "setemit "
@ -136,15 +131,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
break; break;
case OpCode::Type::Arithmetic: case OpCode::Type::Arithmetic:
case OpCode::Type::MultiplyAdd: case OpCode::Type::MultiplyAdd: {
{
// Use custom code for special instructions // Use custom code for special instructions
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
case OpCode::Id::CMP: case OpCode::Id::CMP: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
// NOTE: CMP always writes both cc components, so we do not consider the dest mask here. // NOTE: CMP always writes both cc components, so we do not consider the dest
// mask here.
output << " cc.xy"; output << " cc.xy";
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
@ -152,22 +146,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.common.GetSrc2(false); SourceRegister src2 = instr.common.GetSrc2(false);
output << ' '; output << ' ';
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' '; swizzle.SelectorToString(false).substr(0, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(0, 1), false);
output << ", "; output << ", ";
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' '; swizzle.SelectorToString(false).substr(1, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(1, 1), false);
break; break;
} }
case OpCode::Id::MAD: case OpCode::Id::MAD:
case OpCode::Id::MADI: case OpCode::Id::MADI: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
@ -175,34 +176,42 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted);
SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted);
output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.'
<< swizzle.DestMaskToString();
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); print_input(output, src1, swizzle.negate_src1,
SelectorToString(swizzle.src1_selector));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector)); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector));
} else { } else {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector), true, instr.mad.AddressRegisterName()); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector), true,
instr.mad.AddressRegisterName());
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector), true, instr.mad.AddressRegisterName()); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector), true,
instr.mad.AddressRegisterName());
} else { } else {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector)); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector));
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
break; break;
} }
default: default: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
if (opcode_info.subtype & OpCode::Info::Dest) { if (opcode_info.subtype & OpCode::Info::Dest) {
// e.g. "r12.xy__" // e.g. "r12.xy__"
output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.common.dest.Value().GetName()
<< '.' << swizzle.DestMaskToString();
} else if (opcode_info.subtype == OpCode::Info::MOVA) { } else if (opcode_info.subtype == OpCode::Info::MOVA) {
output << " a0." << swizzle.DestMaskToString(); output << " a0." << swizzle.DestMaskToString();
} }
@ -210,14 +219,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
if (opcode_info.subtype & OpCode::Info::Src1) { if (opcode_info.subtype & OpCode::Info::Src1) {
SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
swizzle.SelectorToString(false), true,
instr.common.AddressRegisterName());
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
// TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 // TODO: In some cases, the Address Register is used as an index for SRC2
// instead of SRC1
if (opcode_info.subtype & OpCode::Info::Src2) { if (opcode_info.subtype & OpCode::Info::Src2) {
SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
break; break;
@ -228,8 +241,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
case OpCode::Type::Conditional: case OpCode::Type::Conditional:
case OpCode::Type::UniformFlowControl: case OpCode::Type::UniformFlowControl: {
{
output << ' '; output << ' ';
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
@ -242,7 +254,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
output << '('; output << '(';
if (instr.flow_control.op != instr.flow_control.JustY) { if (instr.flow_control.op != instr.flow_control.JustY) {
if (instr.flow_control.refx) output << '!'; if (instr.flow_control.refx)
output << '!';
output << "cc.x"; output << "cc.x";
} }
@ -253,7 +266,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
if (instr.flow_control.op != instr.flow_control.JustX) { if (instr.flow_control.op != instr.flow_control.JustX) {
if (instr.flow_control.refy) output << '!'; if (instr.flow_control.refy)
output << '!';
output << "cc.y"; output << "cc.y";
} }
@ -266,17 +280,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
u32 target_addr_else = instr.flow_control.dest_offset; u32 target_addr_else = instr.flow_control.dest_offset;
if (opcode_info.subtype & OpCode::Info::HasAlternative) { if (opcode_info.subtype & OpCode::Info::HasAlternative) {
output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "else jump to 0x" << std::setw(4) << std::right
<< std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset);
} else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) {
output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0')
<< std::hex << (4 * instr.flow_control.dest_offset);
} else { } else {
// TODO: Handle other cases // TODO: Handle other cases
output << "(unknown destination)"; output << "(unknown destination)";
} }
if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex output << " (return on 0x" << std::setw(4) << std::right
<< (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')'; << std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset +
4 * instr.flow_control.num_instructions)
<< ')';
} }
break; break;
@ -300,8 +320,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
case Qt::FontRole: case Qt::FontRole:
return GetMonospaceFont(); return GetMonospaceFont();
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
// Highlight current instruction // Highlight current instruction
int current_record_index = par->cycle_index->value(); int current_record_index = par->cycle_index->value();
if (current_record_index < static_cast<int>(par->debug_data.records.size())) { if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
@ -319,10 +338,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
return QBrush(QColor(192, 192, 192)); return QBrush(QColor(192, 192, 192));
} }
// TODO: Draw arrows for each "reachable" instruction to visualize control flow // TODO: Draw arrows for each "reachable" instruction to visualize control flow
default: default:
break; break;
} }
@ -331,23 +348,24 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
void GraphicsVertexShaderWidget::DumpShader() { void GraphicsVertexShaderWidget::DumpShader() {
QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", QString filename = QFileDialog::getSaveFileName(
tr("Shader Binary (*.shbin)")); this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)"));
if (filename.isEmpty()) { if (filename.isEmpty()) {
// If the user canceled the dialog, don't dump anything. // If the user canceled the dialog, don't dump anything.
return; return;
} }
auto& setup = Pica::g_state.vs; auto& setup = Pica::g_state.vs;
auto& config = Pica::g_state.regs.vs; auto& config = Pica::g_state.regs.vs;
Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup,
Pica::g_state.regs.vs_output_attributes);
} }
GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
QWidget* parent) std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
: BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
setObjectName("PicaVertexShader"); setObjectName("PicaVertexShader");
// Clear input vertex data so that it contains valid float values in case a debug shader // Clear input vertex data so that it contains valid float values in case a debug shader
@ -365,7 +383,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
input_data[i]->setValidator(new QDoubleValidator(input_data[i])); input_data[i]->setValidator(new QDoubleValidator(input_data[i]));
} }
breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); breakpoint_warning =
new QLabel(tr("(data only available at vertex shader invocation breakpoints)"));
// TODO: Add some button for jumping to the shader entry point // TODO: Add some button for jumping to the shader entry point
@ -442,7 +461,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
// Set a minimum height so that the size of this label doesn't cause the rest of the bottom // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
// part of the UI to keep jumping up and down when cycling through instructions. // part of the UI to keep jumping up and down when cycling through instructions.
instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6); instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() *
6);
instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop);
main_layout->addWidget(instruction_description); main_layout->addWidget(instruction_description);
@ -471,7 +491,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
for (unsigned attr = 0; attr < 16; ++attr) { for (unsigned attr = 0; attr < 16; ++attr) {
for (unsigned comp = 0; comp < 4; ++comp) { for (unsigned comp = 0; comp < 4; ++comp) {
input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); input_data[4 * attr + comp]->setText(
QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32()));
} }
} }
breakpoint_warning->hide(); breakpoint_warning->hide();
@ -498,10 +519,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
info.swizzle_info.push_back({pattern}); info.swizzle_info.push_back({pattern});
u32 entry_point = Pica::g_state.regs.vs.main_offset; u32 entry_point = Pica::g_state.regs.vs.main_offset;
info.labels.insert({ entry_point, "main" }); info.labels.insert({entry_point, "main"});
// Generate debug information // Generate debug information
debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config,
shader_setup);
// Reload widget state // Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) { for (int attr = 0; attr < num_attributes; ++attr) {
@ -537,29 +559,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
auto& record = debug_data.records[index]; auto& record = debug_data.records[index];
if (record.mask & Pica::Shader::DebugDataRecord::SRC1) if (record.mask & Pica::Shader::DebugDataRecord::SRC1)
text += tr("SRC1: %1, %2, %3, %4\n").arg(record.src1.x.ToFloat32()).arg(record.src1.y.ToFloat32()).arg(record.src1.z.ToFloat32()).arg(record.src1.w.ToFloat32()); text += tr("SRC1: %1, %2, %3, %4\n")
.arg(record.src1.x.ToFloat32())
.arg(record.src1.y.ToFloat32())
.arg(record.src1.z.ToFloat32())
.arg(record.src1.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC2) if (record.mask & Pica::Shader::DebugDataRecord::SRC2)
text += tr("SRC2: %1, %2, %3, %4\n").arg(record.src2.x.ToFloat32()).arg(record.src2.y.ToFloat32()).arg(record.src2.z.ToFloat32()).arg(record.src2.w.ToFloat32()); text += tr("SRC2: %1, %2, %3, %4\n")
.arg(record.src2.x.ToFloat32())
.arg(record.src2.y.ToFloat32())
.arg(record.src2.z.ToFloat32())
.arg(record.src2.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC3) if (record.mask & Pica::Shader::DebugDataRecord::SRC3)
text += tr("SRC3: %1, %2, %3, %4\n").arg(record.src3.x.ToFloat32()).arg(record.src3.y.ToFloat32()).arg(record.src3.z.ToFloat32()).arg(record.src3.w.ToFloat32()); text += tr("SRC3: %1, %2, %3, %4\n")
.arg(record.src3.x.ToFloat32())
.arg(record.src3.y.ToFloat32())
.arg(record.src3.z.ToFloat32())
.arg(record.src3.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN)
text += tr("DEST_IN: %1, %2, %3, %4\n").arg(record.dest_in.x.ToFloat32()).arg(record.dest_in.y.ToFloat32()).arg(record.dest_in.z.ToFloat32()).arg(record.dest_in.w.ToFloat32()); text += tr("DEST_IN: %1, %2, %3, %4\n")
.arg(record.dest_in.x.ToFloat32())
.arg(record.dest_in.y.ToFloat32())
.arg(record.dest_in.z.ToFloat32())
.arg(record.dest_in.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT)
text += tr("DEST_OUT: %1, %2, %3, %4\n").arg(record.dest_out.x.ToFloat32()).arg(record.dest_out.y.ToFloat32()).arg(record.dest_out.z.ToFloat32()).arg(record.dest_out.w.ToFloat32()); text += tr("DEST_OUT: %1, %2, %3, %4\n")
.arg(record.dest_out.x.ToFloat32())
.arg(record.dest_out.y.ToFloat32())
.arg(record.dest_out.z.ToFloat32())
.arg(record.dest_out.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT)
text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); text += tr("Addres Registers: %1, %2\n")
.arg(record.address_registers[0])
.arg(record.address_registers[1]);
if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT)
text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); text += tr("Compare Result: %1, %2\n")
.arg(record.conditional_code[0] ? "true" : "false")
.arg(record.conditional_code[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN)
text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN)
text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); text += tr("Dynamic Conditions: %1, %2\n")
.arg(record.cond_cmp[0] ? "true" : "false")
.arg(record.cond_cmp[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN)
text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n").arg(record.loop_int.x).arg(record.loop_int.y).arg(record.loop_int.z).arg(record.loop_int.w); text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n")
.arg(record.loop_int.x)
.arg(record.loop_int.y)
.arg(record.loop_int.z)
.arg(record.loop_int.w);
text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); text +=
tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0'));
if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) {
text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0'));
} else { } else {
@ -570,6 +623,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
// Emit model update notification and scroll to current instruction // Emit model update notification and scroll to current instruction
QModelIndex instr_index = model->index(record.instruction_offset, 0); QModelIndex instr_index = model->index(record.instruction_offset, 0);
emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount())); emit model->dataChanged(instr_index,
model->index(record.instruction_offset, model->columnCount()));
binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible);
} }

View file

@ -5,11 +5,9 @@
#pragma once #pragma once
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QTreeView>
#include "citra_qt/debugger/graphics_breakpoint_observer.h" #include "citra_qt/debugger/graphics_breakpoint_observer.h"
#include "nihstro/parser_shbin.h" #include "nihstro/parser_shbin.h"
#include "video_core/shader/shader.h" #include "video_core/shader/shader.h"
class QLabel; class QLabel;
@ -26,7 +24,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
private: private:
GraphicsVertexShaderWidget* par; GraphicsVertexShaderWidget* par;
@ -56,7 +55,9 @@ private slots:
/** /**
* Reload widget based on the current PICA200 state * Reload widget based on the current PICA200 state
* @param replace_vertex_data If true, invalidate all current vertex data * @param replace_vertex_data If true, invalidate all current vertex data
* @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to specify that no valid vertex data can be retrieved currently. Only used if replace_vertex_data is true. * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to
* specify that no valid vertex data can be retrieved currently. Only used if
* replace_vertex_data is true.
*/ */
void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr);
@ -66,9 +67,12 @@ private:
GraphicsVertexShaderModel* model; GraphicsVertexShaderModel* model;
/// TODO: Move these into a single struct /// TODO: Move these into a single struct
std::array<QLineEdit*, 4*16> input_data; // A text box for each of the 4 components of up to 16 vertex attributes std::array<QLineEdit*, 4 * 16>
std::array<QWidget*, 16> input_data_container; // QWidget containing the QLayout containing each vertex attribute input_data; // A text box for each of the 4 components of up to 16 vertex attributes
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute which the vertex attribute maps to std::array<QWidget*, 16>
input_data_container; // QWidget containing the QLayout containing each vertex attribute
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute
// which the vertex attribute maps to
// Text to be shown when input vertex data is not retrievable // Text to be shown when input vertex data is not retrievable
QLabel* breakpoint_warning; QLabel* breakpoint_warning;

View file

@ -5,10 +5,8 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QString> #include <QString>
#include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/profiler.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/profiler_reporting.h" #include "common/profiler_reporting.h"
@ -22,57 +20,58 @@
using namespace Common::Profiling; using namespace Common::Profiling;
static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) {
{
static auto duration_to_float = [](Duration dur) -> float { static auto duration_to_float = [](Duration dur) -> float {
using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
return std::chrono::duration_cast<FloatMs>(dur).count(); return std::chrono::duration_cast<FloatMs>(dur).count();
}; };
switch (col) { switch (col) {
case 1: return duration_to_float(duration.avg); case 1:
case 2: return duration_to_float(duration.min); return duration_to_float(duration.avg);
case 3: return duration_to_float(duration.max); case 2:
default: return QVariant(); return duration_to_float(duration.min);
case 3:
return duration_to_float(duration.max);
default:
return QVariant();
} }
} }
ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) {
{
updateProfilingInfo(); updateProfilingInfo();
} }
QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const {
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) { switch (section) {
case 0: return tr("Category"); case 0:
case 1: return tr("Avg"); return tr("Category");
case 2: return tr("Min"); case 1:
case 3: return tr("Max"); return tr("Avg");
case 2:
return tr("Min");
case 3:
return tr("Max");
} }
} }
return QVariant(); return QVariant();
} }
QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const {
{
return createIndex(row, column); return createIndex(row, column);
} }
QModelIndex ProfilerModel::parent(const QModelIndex& child) const QModelIndex ProfilerModel::parent(const QModelIndex& child) const {
{
return QModelIndex(); return QModelIndex();
} }
int ProfilerModel::columnCount(const QModelIndex& parent) const int ProfilerModel::columnCount(const QModelIndex& parent) const {
{
return 4; return 4;
} }
int ProfilerModel::rowCount(const QModelIndex& parent) const int ProfilerModel::rowCount(const QModelIndex& parent) const {
{
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} else { } else {
@ -80,8 +79,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const
} }
} }
QVariant ProfilerModel::data(const QModelIndex& index, int role) const QVariant ProfilerModel::data(const QModelIndex& index, int role) const {
{
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (index.row() == 0) { if (index.row() == 0) {
if (index.column() == 0) { if (index.column() == 0) {
@ -101,14 +99,12 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
void ProfilerModel::updateProfilingInfo() void ProfilerModel::updateProfilingInfo() {
{
results = GetTimingResultsAggregator()->GetAggregatedResults(); results = GetTimingResultsAggregator()->GetAggregatedResults();
emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3)); emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
} }
ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
model = new ProfilerModel(this); model = new ProfilerModel(this);
@ -118,8 +114,7 @@ ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo())); connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
} }
void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) {
{
if (enable) { if (enable) {
update_timer.start(100); update_timer.start(100);
model->updateProfilingInfo(); model->updateProfilingInfo();
@ -157,9 +152,7 @@ private:
#endif #endif
MicroProfileDialog::MicroProfileDialog(QWidget* parent) MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
: QWidget(parent, Qt::Dialog)
{
setObjectName("MicroProfile"); setObjectName("MicroProfile");
setWindowTitle(tr("MicroProfile")); setWindowTitle(tr("MicroProfile"));
resize(1000, 600); resize(1000, 600);
@ -175,7 +168,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
layout->addWidget(widget); layout->addWidget(widget);
setLayout(layout); setLayout(layout);
// Configure focus so that widget is focusable and the dialog automatically forwards focus to it. // Configure focus so that widget is focusable and the dialog automatically forwards focus to
// it.
setFocusProxy(widget); setFocusProxy(widget);
widget->setFocusPolicy(Qt::StrongFocus); widget->setFocusPolicy(Qt::StrongFocus);
widget->setFocus(); widget->setFocus();
@ -207,7 +201,6 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) {
QWidget::hideEvent(ev); QWidget::hideEvent(ev);
} }
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
@ -308,7 +301,8 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex
} }
} }
void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
MicroProfileBoxType type) {
QColor color = QColor::fromRgba(hex_color); QColor color = QColor::fromRgba(hex_color);
QBrush brush = color; QBrush brush = color;
if (type == MicroProfileBoxTypeBar) { if (type == MicroProfileBoxTypeBar) {
@ -326,7 +320,7 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color)
static std::vector<QPointF> point_buf; static std::vector<QPointF> point_buf;
for (u32 i = 0; i < vertices_length; ++i) { for (u32 i = 0; i < vertices_length; ++i) {
point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
} }
// hex_color does not include an alpha, so it must be assumed to be 255 // hex_color does not include an alpha, so it must be assumed to be 255

View file

@ -7,21 +7,20 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDockWidget> #include <QDockWidget>
#include <QTimer> #include <QTimer>
#include "ui_profiler.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/profiler_reporting.h" #include "common/profiler_reporting.h"
#include "ui_profiler.h"
class ProfilerModel : public QAbstractItemModel class ProfilerModel : public QAbstractItemModel {
{
Q_OBJECT Q_OBJECT
public: public:
ProfilerModel(QObject* parent); ProfilerModel(QObject* parent);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override; QModelIndex parent(const QModelIndex& child) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -34,8 +33,7 @@ private:
Common::Profiling::AggregatedFrameResult results; Common::Profiling::AggregatedFrameResult results;
}; };
class ProfilerWidget : public QDockWidget class ProfilerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:
@ -51,7 +49,6 @@ private:
QTimer update_timer; QTimer update_timer;
}; };
class MicroProfileDialog : public QWidget { class MicroProfileDialog : public QWidget {
Q_OBJECT Q_OBJECT

View file

@ -4,12 +4,9 @@
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
GRamView::GRamView(QWidget* parent) : QHexEdit(parent) GRamView::GRamView(QWidget* parent) : QHexEdit(parent) {}
{
}
void GRamView::OnCPUStepped() void GRamView::OnCPUStepped() {
{
// TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams... // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams...
//setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8)); // setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
} }

View file

@ -4,8 +4,7 @@
#include "qhexedit.h" #include "qhexedit.h"
class GRamView : public QHexEdit class GRamView : public QHexEdit {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -3,12 +3,10 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "core/core.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/core.h"
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
cpu_regs_ui.setupUi(this); cpu_regs_ui.setupUi(this);
@ -16,7 +14,8 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
tree = cpu_regs_ui.treeWidget; tree = cpu_regs_ui.treeWidget;
tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
tree->addTopLevelItem(vfp_system_registers = new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); tree->addTopLevelItem(vfp_system_registers =
new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
@ -63,17 +62,18 @@ void RegistersWidget::OnDebugModeEntered() {
return; return;
for (int i = 0; i < core_registers->childCount(); ++i) for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); core_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); vfp_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues(); UpdateCPSRValues();
UpdateVFPSystemRegisterValues(); UpdateVFPSystemRegisterValues();
} }
void RegistersWidget::OnDebugModeLeft() { void RegistersWidget::OnDebugModeLeft() {}
}
void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
setEnabled(true); setEnabled(true);
@ -130,21 +130,24 @@ void RegistersWidget::UpdateCPSRValues() {
const u32 cpsr_val = Core::g_app_core->GetCPSR(); const u32 cpsr_val = Core::g_app_core->GetCPSR();
cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText(1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode cpsr->child(0)->setText(
cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State 1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
cpsr->child(6)->setText(1, QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM) cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess
cpsr->child(7)->setText(1, QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal cpsr->child(6)->setText(1,
cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle cpsr->child(7)->setText(1,
cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than
} }
void RegistersWidget::CreateVFPSystemRegisterChildren() { void RegistersWidget::CreateVFPSystemRegisterChildren() {
@ -188,9 +191,9 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
} }
void RegistersWidget::UpdateVFPSystemRegisterValues() { void RegistersWidget::UpdateVFPSystemRegisterValues() {
const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR); const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR);
const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC); const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC);
const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST); const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST);
const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2); const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2);
QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
@ -228,6 +231,8 @@ void RegistersWidget::UpdateVFPSystemRegisterValues() {
fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1)); fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1));
fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1)); fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1));
vfp_system_registers->child(2)->setText(1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0'))); vfp_system_registers->child(2)->setText(
vfp_system_registers->child(3)->setText(1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0'))); 1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0')));
vfp_system_registers->child(3)->setText(
1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0')));
} }

View file

@ -2,16 +2,14 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "ui_registers.h"
#include <QDockWidget> #include <QDockWidget>
#include "ui_registers.h"
class QTreeWidget; class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class EmuThread; class EmuThread;
class RegistersWidget : public QDockWidget class RegistersWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -5,19 +5,15 @@
#include <QHeaderView> #include <QHeaderView>
#include <QThreadPool> #include <QThreadPool>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/loader/loader.h"
#include "game_list.h" #include "game_list.h"
#include "game_list_p.h" #include "game_list_p.h"
#include "ui_settings.h" #include "ui_settings.h"
#include "core/loader/loader.h" GameList::GameList(QWidget* parent) {
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
GameList::GameList(QWidget* parent)
{
QVBoxLayout* layout = new QVBoxLayout; QVBoxLayout* layout = new QVBoxLayout;
tree_view = new QTreeView; tree_view = new QTreeView;
@ -38,9 +34,11 @@ GameList::GameList(QWidget* parent)
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); connect(tree_view, SIGNAL(activated(const QModelIndex&)), this,
SLOT(ValidateEntry(const QModelIndex&)));
// We must register all custom types with the Qt Automoc system so that we are able to use it with // We must register all custom types with the Qt Automoc system so that we are able to use it
// with
// signals/slots. In this case, QList falls under the umbrells of custom types. // signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@ -48,18 +46,15 @@ GameList::GameList(QWidget* parent)
setLayout(layout); setLayout(layout);
} }
GameList::~GameList() GameList::~GameList() {
{
emit ShouldCancelWorker(); emit ShouldCancelWorker();
} }
void GameList::AddEntry(QList<QStandardItem*> entry_items) void GameList::AddEntry(QList<QStandardItem*> entry_items) {
{
item_model->invisibleRootItem()->appendRow(entry_items); item_model->invisibleRootItem()->appendRow(entry_items);
} }
void GameList::ValidateEntry(const QModelIndex& item) void GameList::ValidateEntry(const QModelIndex& item) {
{
// We don't care about the individual QStandardItem that was selected, but its row. // We don't care about the individual QStandardItem that was selected, but its row.
int row = item_model->itemFromIndex(item)->row(); int row = item_model->itemFromIndex(item)->row();
QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
@ -73,14 +68,13 @@ void GameList::ValidateEntry(const QModelIndex& item)
emit GameChosen(file_path); emit GameChosen(file_path);
} }
void GameList::DonePopulating() void GameList::DonePopulating() {
{
tree_view->setEnabled(true); tree_view->setEnabled(true);
} }
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
{ if (!FileUtil::Exists(dir_path.toStdString()) ||
if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { !FileUtil::IsDirectory(dir_path.toStdString())) {
LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
return; return;
} }
@ -92,22 +86,22 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
emit ShouldCancelWorker(); emit ShouldCancelWorker();
GameListWorker* worker = new GameListWorker(dir_path, deep_scan); GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this,
SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection);
connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection);
// Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel without delay. // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel
// without delay.
connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection);
QThreadPool::globalInstance()->start(worker); QThreadPool::globalInstance()->start(worker);
current_worker = std::move(worker); current_worker = std::move(worker);
} }
void GameList::SaveInterfaceLayout() void GameList::SaveInterfaceLayout() {
{
UISettings::values.gamelist_header_state = tree_view->header()->saveState(); UISettings::values.gamelist_header_state = tree_view->header()->saveState();
} }
void GameList::LoadInterfaceLayout() void GameList::LoadInterfaceLayout() {
{
auto header = tree_view->header(); auto header = tree_view->header();
if (!header->restoreState(UISettings::values.gamelist_header_state)) { if (!header->restoreState(UISettings::values.gamelist_header_state)) {
// We are using the name column to display icons and titles // We are using the name column to display icons and titles
@ -118,10 +112,8 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
} }
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
{ const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
const auto callback = [this, recursion](unsigned* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name; std::string physical_name = directory + DIR_SEP + virtual_name;
@ -138,7 +130,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
emit EntryReady({ emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh), new GameListItemPath(QString::fromStdString(physical_name), smdh),
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)), new GameListItemSize(FileUtil::GetSize(physical_name)),
}); });
} else if (recursion > 0) { } else if (recursion > 0) {
@ -151,15 +144,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
} }
void GameListWorker::run() void GameListWorker::run() {
{
stop_processing = false; stop_processing = false;
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished(); emit Finished();
} }
void GameListWorker::Cancel() void GameListWorker::Cancel() {
{
disconnect(this, 0, 0, 0); disconnect(this, 0, 0, 0);
stop_processing = true; stop_processing = true;
} }

View file

@ -14,7 +14,6 @@
class GameListWorker; class GameListWorker;
class GameList : public QWidget { class GameList : public QWidget {
Q_OBJECT Q_OBJECT

View file

@ -5,18 +5,14 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <QImage> #include <QImage>
#include <QRunnable> #include <QRunnable>
#include <QStandardItem> #include <QStandardItem>
#include <QString> #include <QString>
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/string_util.h"
#include "common/color.h" #include "common/color.h"
#include "common/string_util.h"
#include "core/loader/smdh.h" #include "core/loader/smdh.h"
#include "video_core/utils.h" #include "video_core/utils.h"
/** /**
@ -51,19 +47,19 @@ static QPixmap GetDefaultIcon(bool large) {
* @param language title language * @param language title language
* @return QString short title * @return QString short title
*/ */
static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh,
Loader::SMDH::TitleLanguage language) {
return QString::fromUtf16(smdh.GetShortTitle(language).data()); return QString::fromUtf16(smdh.GetShortTitle(language).data());
} }
class GameListItem : public QStandardItem { class GameListItem : public QStandardItem {
public: public:
GameListItem(): QStandardItem() {} GameListItem() : QStandardItem() {}
GameListItem(const QString& string): QStandardItem(string) {} GameListItem(const QString& string) : QStandardItem(string) {}
virtual ~GameListItem() override {} virtual ~GameListItem() override {}
}; };
/** /**
* A specialization of GameListItem for path values. * A specialization of GameListItem for path values.
* This class ensures that for every full path value it holds, a correct string representation * This class ensures that for every full path value it holds, a correct string representation
@ -76,9 +72,8 @@ public:
static const int FullPathRole = Qt::UserRole + 1; static const int FullPathRole = Qt::UserRole + 1;
static const int TitleRole = Qt::UserRole + 2; static const int TitleRole = Qt::UserRole + 2;
GameListItemPath(): GameListItem() {} GameListItemPath() : GameListItem() {}
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem() GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() {
{
setData(game_path, FullPathRole); setData(game_path, FullPathRole);
if (!Loader::IsValidSMDH(smdh_data)) { if (!Loader::IsValidSMDH(smdh_data)) {
@ -94,13 +89,15 @@ public:
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
// Get title form SMDH // Get title form SMDH
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English),
TitleRole);
} }
QVariant data(int role) const override { QVariant data(int role) const override {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
std::string filename; std::string filename;
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
nullptr);
QString title = data(TitleRole).toString(); QString title = data(TitleRole).toString();
return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title); return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
} else { } else {
@ -109,7 +106,6 @@ public:
} }
}; };
/** /**
* A specialization of GameListItem for size values. * A specialization of GameListItem for size values.
* This class ensures that for every numerical size value it holds (in bytes), a correct * This class ensures that for every numerical size value it holds (in bytes), a correct
@ -120,14 +116,12 @@ class GameListItemSize : public GameListItem {
public: public:
static const int SizeRole = Qt::UserRole + 1; static const int SizeRole = Qt::UserRole + 1;
GameListItemSize(): GameListItem() {} GameListItemSize() : GameListItem() {}
GameListItemSize(const qulonglong size_bytes): GameListItem() GameListItemSize(const qulonglong size_bytes) : GameListItem() {
{
setData(size_bytes, SizeRole); setData(size_bytes, SizeRole);
} }
void setData(const QVariant& value, int role) override void setData(const QVariant& value, int role) override {
{
// By specializing setData for SizeRole, we can ensure that the numerical and string // By specializing setData for SizeRole, we can ensure that the numerical and string
// representations of the data are always accurate and in the correct format. // representations of the data are always accurate and in the correct format.
if (role == SizeRole) { if (role == SizeRole) {
@ -141,15 +135,14 @@ public:
/** /**
* This operator is, in practice, only used by the TreeView sorting systems. * This operator is, in practice, only used by the TreeView sorting systems.
* Override it so that it will correctly sort by numerical value instead of by string representation. * Override it so that it will correctly sort by numerical value instead of by string
* representation.
*/ */
bool operator<(const QStandardItem& other) const override bool operator<(const QStandardItem& other) const override {
{
return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
} }
}; };
/** /**
* Asynchronous worker object for populating the game list. * Asynchronous worker object for populating the game list.
* Communicates with other threads through Qt's signal/slot system. * Communicates with other threads through Qt's signal/slot system.
@ -158,8 +151,8 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
GameListWorker(QString dir_path, bool deep_scan): GameListWorker(QString dir_path, bool deep_scan)
QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
public slots: public slots:
/// Starts the processing of directory tree information. /// Starts the processing of directory tree information.

View file

@ -3,16 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <map> #include <map>
#include <QtGlobal>
#include <QKeySequence> #include <QKeySequence>
#include <QShortcut> #include <QShortcut>
#include <QtGlobal>
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
struct Hotkey struct Hotkey {
{
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
QKeySequence keyseq; QKeySequence keyseq;
@ -25,33 +22,28 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
HotkeyGroupMap hotkey_groups; HotkeyGroupMap hotkey_groups;
void SaveHotkeys() void SaveHotkeys() {
{
UISettings::values.shortcuts.clear(); UISettings::values.shortcuts.clear();
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{ for (auto hotkey : group.second) {
for (auto hotkey : group.second)
{
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(
UISettings::Shortcut(group.first + "/" + hotkey.first, UISettings::Shortcut(group.first + "/" + hotkey.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
hotkey.second.context))); hotkey.second.context)));
} }
} }
} }
void LoadHotkeys() void LoadHotkeys() {
{ // Make sure NOT to use a reference here because it would become invalid once we call
// Make sure NOT to use a reference here because it would become invalid once we call beginGroup() // beginGroup()
for (auto shortcut : UISettings::values.shortcuts) for (auto shortcut : UISettings::values.shortcuts) {
{
QStringList cat = shortcut.first.split("/"); QStringList cat = shortcut.first.split("/");
Q_ASSERT(cat.size() >= 2); Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters // RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) if (!shortcut.second.first.isEmpty()) {
{
hk.keyseq = QKeySequence::fromString(shortcut.second.first); hk.keyseq = QKeySequence::fromString(shortcut.second.first);
hk.context = (Qt::ShortcutContext)shortcut.second.second; hk.context = (Qt::ShortcutContext)shortcut.second.second;
} }
@ -60,17 +52,15 @@ void LoadHotkeys()
} }
} }
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
{ Qt::ShortcutContext default_context) {
if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
{
hotkey_groups[group][action].keyseq = default_keyseq; hotkey_groups[group][action].keyseq = default_keyseq;
hotkey_groups[group][action].context = default_context; hotkey_groups[group][action].context = default_context;
} }
} }
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) {
{
Hotkey& hk = hotkey_groups[group][action]; Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut) if (!hk.shortcut)
@ -79,16 +69,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
return hk.shortcut; return hk.shortcut;
} }
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
{
ui.setupUi(this); ui.setupUi(this);
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
for (auto hotkey : group.second) for (auto hotkey : group.second) {
{
QStringList columns; QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString(); columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns); QTreeWidgetItem* item = new QTreeWidgetItem(columns);

View file

@ -16,36 +16,42 @@ class QShortcut;
* *
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
* @param action Name of the action (e.g. "Start Emulation", "Load Image") * @param action Name of the action (e.g. "Start Emulation", "Load Image")
* @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings file before * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings
* @param default_context Default context to assign if the hotkey wasn't present in the settings file before * file before
* @param default_context Default context to assign if the hotkey wasn't present in the settings
* file before
* @warning Both the group and action strings will be displayed in the hotkey settings dialog * @warning Both the group and action strings will be displayed in the hotkey settings dialog
*/ */
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq = QKeySequence(), Qt::ShortcutContext default_context = Qt::WindowShortcut); void RegisterHotkey(const QString& group, const QString& action,
const QKeySequence& default_keyseq = QKeySequence(),
Qt::ShortcutContext default_context = Qt::WindowShortcut);
/** /**
* Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
* *
* @param widget Parent widget of the returned QShortcut. * @param widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
*/ */
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/** /**
* Saves all registered hotkeys to the settings file. * Saves all registered hotkeys to the settings file.
* *
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context. * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* settings group will be created to store the key sequence and the hotkey context.
*/ */
void SaveHotkeys(); void SaveHotkeys();
/** /**
* Loads hotkeys from the settings file. * Loads hotkeys from the settings file.
* *
* @note Yet unregistered hotkeys which are present in the settings will automatically be registered. * @note Yet unregistered hotkeys which are present in the settings will automatically be
* registered.
*/ */
void LoadHotkeys(); void LoadHotkeys();
class GHotkeysDialog : public QWidget class GHotkeysDialog : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -5,25 +5,15 @@
#include <clocale> #include <clocale>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <glad/glad.h> #include <glad/glad.h>
#define QT_NO_OPENGL #define QT_NO_OPENGL
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QtGui>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include "qhexedit.h" #include <QtGui>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/configure_dialog.h" #include "citra_qt/configure_dialog.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
#include "citra_qt/ui_settings.h"
// Debugger
#include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/callstack.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
#include "citra_qt/debugger/graphics.h" #include "citra_qt/debugger/graphics.h"
@ -35,28 +25,29 @@
#include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/profiler.h"
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
#include "citra_qt/ui_settings.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/platform.h" #include "common/platform.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "core/core.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/arm/disassembler/load_symbol_map.h" #include "core/arm/disassembler/load_symbol_map.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "core/system.h"
#include "qhexedit.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
{
Pica::g_debug_context = Pica::DebugContext::Construct(); Pica::g_debug_context = Pica::DebugContext::Construct();
ui.setupUi(this); ui.setupUi(this);
@ -91,7 +82,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsWidget = new GPUCommandStreamWidget(this); graphicsWidget = new GPUCommandStreamWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
graphicsWidget ->hide(); graphicsWidget->hide();
graphicsCommandsWidget = new GPUCommandListWidget(this); graphicsCommandsWidget = new GPUCommandListWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
@ -110,7 +101,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsTracingWidget->hide(); graphicsTracingWidget->hide();
auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer())); connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
SLOT(OnCreateGraphicsSurfaceViewer()));
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction); debug_menu->addAction(graphicsSurfaceViewerAction);
@ -167,35 +159,44 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
UpdateRecentFiles(); UpdateRecentFiles();
// Setup connections // Setup connections
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
Qt::DirectConnection);
connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection); connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),
Qt::DirectConnection);
connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this,
SLOT(OnMenuSelectGameListRoot()));
connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
// Setup hotkeys // Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open); RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation"); RegisterHotkey("Main Window", "Start Emulation");
LoadHotkeys(); LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
SLOT(OnStartGame()));
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(window_title.c_str()); setWindowTitle(window_title.c_str());
show(); show();
@ -208,8 +209,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
} }
} }
GMainWindow::~GMainWindow() GMainWindow::~GMainWindow() {
{
// will get automatically deleted otherwise // will get automatically deleted otherwise
if (render_window->parent() == nullptr) if (render_window->parent() == nullptr)
delete render_window; delete render_window;
@ -217,19 +217,18 @@ GMainWindow::~GMainWindow()
Pica::g_debug_context.reset(); Pica::g_debug_context.reset();
} }
void GMainWindow::OnDisplayTitleBars(bool show) void GMainWindow::OnDisplayTitleBars(bool show) {
{
QList<QDockWidget*> widgets = findChildren<QDockWidget*>(); QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
if (show) { if (show) {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(nullptr); widget->setTitleBarWidget(nullptr);
if (old != nullptr) if (old != nullptr)
delete old; delete old;
} }
} else { } else {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(new QWidget()); widget->setTitleBarWidget(new QWidget());
if (old != nullptr) if (old != nullptr)
@ -249,7 +248,8 @@ bool GMainWindow::InitializeSystem() {
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
return false; return false;
} }
@ -260,7 +260,8 @@ bool GMainWindow::InitializeSystem() {
case System::Result::ErrorInitVideoCore: case System::Result::ErrorInitVideoCore:
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
break; break;
default: default:
@ -293,8 +294,12 @@ bool GMainWindow::LoadROM(const std::string& filename) {
QMessageBox popup_error; QMessageBox popup_error;
popup_error.setTextFormat(Qt::RichText); popup_error.setTextFormat(Qt::RichText);
popup_error.setWindowTitle(tr("Error while loading ROM!")); popup_error.setWindowTitle(tr("Error while loading ROM!"));
popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>" popup_error.setText(
"For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); tr("The game that you are trying to load must be decrypted before being used with "
"Citra.<br/><br/>"
"For more information on dumping and decrypting games, please see: <a "
"href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://"
"citra-emu.org/wiki/Dumping-Game-Cartridges</a>"));
popup_error.setIcon(QMessageBox::Critical); popup_error.setIcon(QMessageBox::Critical);
popup_error.exec(); popup_error.exec();
break; break;
@ -306,8 +311,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
case Loader::ResultStatus::Error: case Loader::ResultStatus::Error:
default: default:
QMessageBox::critical(this, tr("Error while loading ROM!"), QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!"));
tr("Unknown error!"));
break; break;
} }
return false; return false;
@ -332,13 +336,20 @@ void GMainWindow::BootGame(const std::string& filename) {
emu_thread->start(); emu_thread->start();
connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame()));
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); // before the CPU continues
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()),
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget,
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget,
SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
// Update the GUI // Update the GUI
registersWidget->OnDebugModeEntered(); registersWidget->OnDebugModeEntered();
@ -393,10 +404,12 @@ void GMainWindow::StoreRecentFile(const std::string& filename) {
} }
void GMainWindow::UpdateRecentFiles() { void GMainWindow::UpdateRecentFiles() {
unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); unsigned int num_recent_files =
std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
for (unsigned int i = 0; i < num_recent_files; i++) { for (unsigned int i = 0; i < num_recent_files; i++) {
QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName()); QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text); actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]); actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
@ -420,7 +433,9 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
} }
void GMainWindow::OnMenuLoadFile() { void GMainWindow::OnMenuLoadFile() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); QString filename =
QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.roms_path = QFileInfo(filename).path(); UISettings::values.roms_path = QFileInfo(filename).path();
@ -429,7 +444,8 @@ void GMainWindow::OnMenuLoadFile() {
} }
void GMainWindow::OnMenuLoadSymbolMap() { void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); QString filename = QFileDialog::getOpenFileName(
this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.symbols_path = QFileInfo(filename).path(); UISettings::values.symbols_path = QFileInfo(filename).path();
@ -455,7 +471,8 @@ void GMainWindow::OnMenuRecentFile() {
BootGame(filename.toStdString()); BootGame(filename.toStdString());
} else { } else {
// Display an error message and remove the file from the list. // Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); QMessageBox::information(this, tr("File not found"),
tr("File \"%1\" not found").arg(filename));
UISettings::values.recent_files.removeOne(filename); UISettings::values.recent_files.removeOne(filename);
UpdateRecentFiles(); UpdateRecentFiles();
@ -512,8 +529,7 @@ void GMainWindow::ToggleWindowMode() {
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this); ConfigureDialog configureDialog(this);
auto result = configureDialog.exec(); auto result = configureDialog.exec();
if (result == QDialog::Accepted) if (result == QDialog::Accepted) {
{
configureDialog.applyConfiguration(); configureDialog.applyConfiguration();
render_window->ReloadSetKeymaps(); render_window->ReloadSetKeymaps();
config->Save(); config->Save();
@ -531,9 +547,9 @@ bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;
auto answer = QMessageBox::question(this, tr("Citra"), auto answer =
tr("Are you sure you want to close Citra?"), QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No); QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No; return answer != QMessageBox::No;
} }
@ -575,9 +591,7 @@ int main(int argc, char* argv[]) {
Log::SetFilter(&log_filter); Log::SetFilter(&log_filter);
MicroProfileOnThreadCreate("Frontend"); MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ SCOPE_EXIT({ MicroProfileShutdown(); });
MicroProfileShutdown();
});
// Init settings params // Init settings params
QCoreApplication::setOrganizationName("Citra team"); QCoreApplication::setOrganizationName("Citra team");
@ -586,7 +600,8 @@ int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv); QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders // Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
GMainWindow main_window; GMainWindow main_window;

View file

@ -7,7 +7,6 @@
#include <memory> #include <memory>
#include <QMainWindow> #include <QMainWindow>
#include "ui_main.h" #include "ui_main.h"
class Config; class Config;
@ -23,11 +22,11 @@ class CallstackWidget;
class GPUCommandStreamWidget; class GPUCommandStreamWidget;
class GPUCommandListWidget; class GPUCommandListWidget;
class GMainWindow : public QMainWindow class GMainWindow : public QMainWindow {
{
Q_OBJECT Q_OBJECT
static const int max_recent_files_item = 10; ///< Max number of recently loaded items to keep track /// Max number of recently loaded items to keep track of
static const int max_recent_files_item = 10;
// TODO: Make use of this! // TODO: Make use of this!
enum { enum {

View file

@ -7,5 +7,4 @@
namespace UISettings { namespace UISettings {
Values values = {}; Values values = {};
} }

View file

@ -4,15 +4,14 @@
#pragma once #pragma once
#include <QByteArray>
#include <QStringList>
#include <QString>
#include <vector> #include <vector>
#include <QByteArray>
#include <QString>
#include <QStringList>
namespace UISettings { namespace UISettings {
using ContextualShortcut = std::pair<QString, int> ; using ContextualShortcut = std::pair<QString, int>;
using Shortcut = std::pair<QString, ContextualShortcut>; using Shortcut = std::pair<QString, ContextualShortcut>;
struct Values { struct Values {
@ -43,5 +42,4 @@ struct Values {
}; };
extern Values values; extern Values values;
} }

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -32,12 +31,11 @@
#include <cstdlib> #include <cstdlib>
#include <QLineEdit> #include <QLineEdit>
#include <QRegExpValidator> #include <QRegExpValidator>
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/assert.h" #include "common/assert.h"
CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) CSpinBox::CSpinBox(QWidget* parent)
{ : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) {
// TODO: Might be nice to not immediately call the slot. // TODO: Might be nice to not immediately call the slot.
// Think of an address that is being replaced by a different one, in which case a lot // Think of an address that is being replaced by a different one, in which case a lot
// invalid intermediate addresses would be read from during editing. // invalid intermediate addresses would be read from during editing.
@ -46,8 +44,7 @@ CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100),
UpdateText(); UpdateText();
} }
void CSpinBox::SetValue(qint64 val) void CSpinBox::SetValue(qint64 val) {
{
auto old_value = value; auto old_value = value;
value = std::max(std::min(val, max_value), min_value); value = std::max(std::min(val, max_value), min_value);
@ -57,8 +54,7 @@ void CSpinBox::SetValue(qint64 val)
} }
} }
void CSpinBox::SetRange(qint64 min, qint64 max) void CSpinBox::SetRange(qint64 min, qint64 max) {
{
min_value = min; min_value = min;
max_value = max; max_value = max;
@ -66,8 +62,7 @@ void CSpinBox::SetRange(qint64 min, qint64 max)
UpdateText(); UpdateText();
} }
void CSpinBox::stepBy(int steps) void CSpinBox::stepBy(int steps) {
{
auto new_value = value; auto new_value = value;
// Scale number of steps by the currently selected digit // Scale number of steps by the currently selected digit
// TODO: Move this code elsewhere and enable it. // TODO: Move this code elsewhere and enable it.
@ -93,8 +88,7 @@ void CSpinBox::stepBy(int steps)
UpdateText(); UpdateText();
} }
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const {
{
StepEnabled ret = StepNone; StepEnabled ret = StepNone;
if (value > min_value) if (value > min_value)
@ -106,29 +100,25 @@ QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
return ret; return ret;
} }
void CSpinBox::SetBase(int base) void CSpinBox::SetBase(int base) {
{
this->base = base; this->base = base;
UpdateText(); UpdateText();
} }
void CSpinBox::SetNumDigits(int num_digits) void CSpinBox::SetNumDigits(int num_digits) {
{
this->num_digits = num_digits; this->num_digits = num_digits;
UpdateText(); UpdateText();
} }
void CSpinBox::SetPrefix(const QString& prefix) void CSpinBox::SetPrefix(const QString& prefix) {
{
this->prefix = prefix; this->prefix = prefix;
UpdateText(); UpdateText();
} }
void CSpinBox::SetSuffix(const QString& suffix) void CSpinBox::SetSuffix(const QString& suffix) {
{
this->suffix = suffix; this->suffix = suffix;
UpdateText(); UpdateText();
@ -161,8 +151,7 @@ static QString StringToInputMask(const QString& input) {
return mask; return mask;
} }
void CSpinBox::UpdateText() void CSpinBox::UpdateText() {
{
// If a fixed number of digits is used, we put the line edit in insertion mode by setting an // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
// input mask. // input mask.
QString mask; QString mask;
@ -179,10 +168,9 @@ void CSpinBox::UpdateText()
// The greatest signed 64-bit number has 19 decimal digits. // The greatest signed 64-bit number has 19 decimal digits.
// TODO: Could probably make this more generic with some logarithms. // TODO: Could probably make this more generic with some logarithms.
// For reference, unsigned 64-bit can have up to 20 decimal digits. // For reference, unsigned 64-bit can have up to 20 decimal digits.
int digits = (num_digits != 0) ? num_digits int digits = (num_digits != 0)
: (base == 16) ? 16 ? num_digits
: (base == 10) ? 19 : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case...
: 0xFF; // fallback case...
// Match num_digits digits // Match num_digits digits
// Digits irrelevant to the chosen number base are filtered in the validator // Digits irrelevant to the chosen number base are filtered in the validator
@ -203,29 +191,24 @@ void CSpinBox::UpdateText()
lineEdit()->setCursorPosition(cursor_position); lineEdit()->setCursorPosition(cursor_position);
} }
QString CSpinBox::TextFromValue() QString CSpinBox::TextFromValue() {
{ return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") +
return prefix QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() +
+ QString(HasSign() ? ((value < 0) ? "-" : "+") : "") suffix;
+ QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ suffix;
} }
qint64 CSpinBox::ValueFromText() qint64 CSpinBox::ValueFromText() {
{
unsigned strpos = prefix.length(); unsigned strpos = prefix.length();
QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
return num_string.toLongLong(nullptr, base); return num_string.toLongLong(nullptr, base);
} }
bool CSpinBox::HasSign() const bool CSpinBox::HasSign() const {
{
return base == 10 && min_value < 0; return base == 10 && min_value < 0;
} }
void CSpinBox::OnEditingFinished() void CSpinBox::OnEditingFinished() {
{
// Only update for valid input // Only update for valid input
QString input = lineEdit()->text(); QString input = lineEdit()->text();
int pos = 0; int pos = 0;
@ -233,8 +216,7 @@ void CSpinBox::OnEditingFinished()
SetValue(ValueFromText()); SetValue(ValueFromText());
} }
QValidator::State CSpinBox::validate(QString& input, int& pos) const QValidator::State CSpinBox::validate(QString& input, int& pos) const {
{
if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
return QValidator::Invalid; return QValidator::Invalid;

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,7 +28,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <QAbstractSpinBox> #include <QAbstractSpinBox>

View file

@ -4,7 +4,6 @@
#include <array> #include <array>
#include <cmath> #include <cmath>
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
QFont GetMonospaceFont() { QFont GetMonospaceFont() {
@ -16,10 +15,12 @@ QFont GetMonospaceFont() {
} }
QString ReadableByteSize(qulonglong size) { QString ReadableByteSize(qulonglong size) {
static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" }; static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
if (size == 0) if (size == 0)
return "0"; return "0";
int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size())); int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)),
return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1) static_cast<int>(units.size()));
.arg(units[digit_groups]); return QString("%L1 %2")
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
.arg(units[digit_groups]);
} }

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstdlib> #include <cstdlib>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -18,25 +17,29 @@
// enough for our purposes. // enough for our purposes.
template <typename Fn> template <typename Fn>
#if defined(_MSC_VER) #if defined(_MSC_VER)
__declspec(noinline, noreturn) __declspec(noinline, noreturn)
#elif defined(__GNUC__) #elif defined(__GNUC__)
__attribute__((noinline, noreturn, cold)) __attribute__((noinline, noreturn, cold))
#endif #endif
static void assert_noinline_call(const Fn& fn) { static void assert_noinline_call(const Fn& fn) {
fn(); fn();
Crash(); Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning exit(1); // Keeps GCC's mouth shut about this actually returning
} }
#define ASSERT(_a_) \ #define ASSERT(_a_) \
do if (!(_a_)) { assert_noinline_call([] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!"); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
} \
while (0)
#define ASSERT_MSG(_a_, ...) \ #define ASSERT_MSG(_a_, ...) \
do if (!(_a_)) { assert_noinline_call([&] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
} \
while (0)
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
@ -50,4 +53,4 @@ static void assert_noinline_call(const Fn& fn) {
#endif #endif
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") #define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__) #define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,13 +28,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include "common/common_funcs.h" #include "common/common_funcs.h"
/* /*
@ -111,9 +108,8 @@
* symptoms. * symptoms.
*/ */
#pragma pack(1) #pragma pack(1)
template<std::size_t position, std::size_t bits, typename T> template <std::size_t position, std::size_t bits, typename T>
struct BitField struct BitField {
{
private: private:
// We hide the copy assigment operator here, because the default copy // We hide the copy assigment operator here, because the default copy
// assignment would copy the full storage value, rather than just the bits // assignment would copy the full storage value, rather than just the bits
@ -141,13 +137,10 @@ public:
} }
FORCE_INLINE T Value() const { FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed) if (std::numeric_limits<T>::is_signed) {
{ std::size_t shift = 8 * sizeof(T) - bits;
std::size_t shift = 8 * sizeof(T)-bits;
return (T)((storage << (shift - position)) >> shift); return (T)((storage << (shift - position)) >> shift);
} } else {
else
{
return (T)((storage & GetMask()) >> position); return (T)((storage & GetMask()) >> position);
} }
} }
@ -162,15 +155,14 @@ private:
// T is an enumeration. Note that T is wrapped within an enable_if in the // T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using // former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly. // std::underlying_type<T>::type directly.
typedef typename std::conditional < std::is_enum<T>::value, typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::underlying_type<T>, std::enable_if<true, T>>::type::type StorageType;
std::enable_if < true, T >> ::type::type StorageType;
// Unsigned version of StorageType // Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU; typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
FORCE_INLINE StorageType GetMask() const { FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
} }
StorageType storage; StorageType storage;
@ -186,5 +178,6 @@ private:
#pragma pack() #pragma pack()
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, "BitField must be trivially copyable"); static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value,
"BitField must be trivially copyable");
#endif #endif

View file

@ -18,49 +18,60 @@ namespace Common {
#ifdef _WIN32 #ifdef _WIN32
template <typename T> template <typename T>
static inline int CountSetBits(T v) static inline int CountSetBits(T v) {
{
// from https://graphics.stanford.edu/~seander/bithacks.html // from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual // GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on // POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T)~(T)0/3); v = v - ((v >> 1) & (T) ~(T)0 / 3);
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
v = (v + (v >> 4)) & (T)~(T)0/255*15; v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
} }
static inline int LeastSignificantSetBit(u8 val) static inline int LeastSignificantSetBit(u8 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u16 val) static inline int LeastSignificantSetBit(u16 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u32 val) static inline int LeastSignificantSetBit(u32 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u64 val) static inline int LeastSignificantSetBit(u64 val) {
{
unsigned long index; unsigned long index;
_BitScanForward64(&index, val); _BitScanForward64(&index, val);
return (int)index; return (int)index;
} }
#else #else
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); } static inline int CountSetBits(u8 val) {
static inline int CountSetBits(u16 val) { return __builtin_popcount(val); } return __builtin_popcount(val);
static inline int CountSetBits(u32 val) { return __builtin_popcount(val); } }
static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); } static inline int CountSetBits(u16 val) {
static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); } return __builtin_popcount(val);
static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); } }
static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); } static inline int CountSetBits(u32 val) {
static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); } return __builtin_popcount(val);
}
static inline int CountSetBits(u64 val) {
return __builtin_popcountll(val);
}
static inline int LeastSignificantSetBit(u8 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u16 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u32 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u64 val) {
return __builtin_ctzll(val);
}
#endif #endif
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e. // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
@ -84,57 +95,62 @@ static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val);
// TODO: use constexpr when MSVC gets out of the Dark Ages // TODO: use constexpr when MSVC gets out of the Dark Ages
template <typename IntTy> template <typename IntTy>
class BitSet class BitSet {
{
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public: public:
// A reference to a particular bit, returned from operator[]. // A reference to a particular bit, returned from operator[].
class Ref class Ref {
{
public: public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
operator bool() const { return (m_bs->m_val & m_mask) != 0; } operator bool() const {
bool operator=(bool set) return (m_bs->m_val & m_mask) != 0;
{ }
bool operator=(bool set) {
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set; return set;
} }
private: private:
BitSet* m_bs; BitSet* m_bs;
IntTy m_mask; IntTy m_mask;
}; };
// A STL-like iterator is required to be able to use range-based for loops. // A STL-like iterator is required to be able to use range-based for loops.
class Iterator class Iterator {
{
public: public:
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {} Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; } Iterator& operator=(Iterator other) {
int operator*() { return m_bit; } new (this) Iterator(other);
Iterator& operator++() return *this;
{ }
if (m_val == 0) int operator*() {
{ return m_bit;
}
Iterator& operator++() {
if (m_val == 0) {
m_bit = -1; m_bit = -1;
} } else {
else
{
int bit = LeastSignificantSetBit(m_val); int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit); m_val &= ~(1 << bit);
m_bit = bit; m_bit = bit;
} }
return *this; return *this;
} }
Iterator operator++(int _) Iterator operator++(int _) {
{
Iterator other(*this); Iterator other(*this);
++*this; ++*this;
return other; return other;
} }
bool operator==(Iterator other) const { return m_bit == other.m_bit; } bool operator==(Iterator other) const {
bool operator!=(Iterator other) const { return m_bit != other.m_bit; } return m_bit == other.m_bit;
}
bool operator!=(Iterator other) const {
return m_bit != other.m_bit;
}
private: private:
IntTy m_val; IntTy m_val;
int m_bit; int m_bit;
@ -142,42 +158,75 @@ public:
BitSet() : m_val(0) {} BitSet() : m_val(0) {}
explicit BitSet(IntTy val) : m_val(val) {} explicit BitSet(IntTy val) : m_val(val) {}
BitSet(std::initializer_list<int> init) BitSet(std::initializer_list<int> init) {
{
m_val = 0; m_val = 0;
for (int bit : init) for (int bit : init)
m_val |= (IntTy)1 << bit; m_val |= (IntTy)1 << bit;
} }
static BitSet AllTrue(size_t count) static BitSet AllTrue(size_t count) {
{ return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
} }
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } Ref operator[](size_t bit) {
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; } return Ref(this, (IntTy)1 << bit);
bool operator==(BitSet other) const { return m_val == other.m_val; } }
bool operator!=(BitSet other) const { return m_val != other.m_val; } const Ref operator[](size_t bit) const {
bool operator<(BitSet other) const { return m_val < other.m_val; } return (*const_cast<BitSet*>(this))[bit];
bool operator>(BitSet other) const { return m_val > other.m_val; } }
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); } bool operator==(BitSet other) const {
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); } return m_val == other.m_val;
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); } }
BitSet operator~() const { return BitSet(~m_val); } bool operator!=(BitSet other) const {
BitSet& operator|=(BitSet other) { return *this = *this | other; } return m_val != other.m_val;
BitSet& operator&=(BitSet other) { return *this = *this & other; } }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; } bool operator<(BitSet other) const {
return m_val < other.m_val;
}
bool operator>(BitSet other) const {
return m_val > other.m_val;
}
BitSet operator|(BitSet other) const {
return BitSet(m_val | other.m_val);
}
BitSet operator&(BitSet other) const {
return BitSet(m_val & other.m_val);
}
BitSet operator^(BitSet other) const {
return BitSet(m_val ^ other.m_val);
}
BitSet operator~() const {
return BitSet(~m_val);
}
BitSet& operator|=(BitSet other) {
return *this = *this | other;
}
BitSet& operator&=(BitSet other) {
return *this = *this & other;
}
BitSet& operator^=(BitSet other) {
return *this = *this ^ other;
}
operator u32() = delete; operator u32() = delete;
operator bool() { return m_val != 0; } operator bool() {
return m_val != 0;
}
// Warning: Even though on modern CPUs this is a single fast instruction, // Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86, // Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally // so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop. // be faster than a loop.
unsigned int Count() const { return CountSetBits(m_val); } unsigned int Count() const {
return CountSetBits(m_val);
}
Iterator begin() const { Iterator it(m_val, 0); return ++it; } Iterator begin() const {
Iterator end() const { return Iterator(m_val, -1); } Iterator it(m_val, 0);
return ++it;
}
Iterator end() const {
return Iterator(m_val, -1);
}
IntTy m_val; IntTy m_val;
}; };

View file

@ -2,33 +2,29 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <sstream>
#include "common/break_points.h" #include "common/break_points.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <sstream> bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
#include <algorithm>
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const
{
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
{ auto cond = [&iAddress](const TBreakPoint& bp) {
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; }; return bp.iAddress == iAddress && bp.bTemporary;
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
{
TBreakPointsStr bps; TBreakPointsStr bps;
for (auto breakpoint : m_BreakPoints) for (auto breakpoint : m_BreakPoints) {
{ if (!breakpoint.bTemporary) {
if (!breakpoint.bTemporary)
{
std::stringstream bp; std::stringstream bp;
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
bps.push_back(bp.str()); bps.push_back(bp.str());
@ -38,10 +34,8 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
return bps; return bps;
} }
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
{ for (auto bps_item : bps) {
for (auto bps_item : bps)
{
TBreakPoint bp; TBreakPoint bp;
std::stringstream bpstr; std::stringstream bpstr;
bpstr << std::hex << bps_item; bpstr << std::hex << bps_item;
@ -52,18 +46,15 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
} }
} }
void BreakPoints::Add(const TBreakPoint& bp) void BreakPoints::Add(const TBreakPoint& bp) {
{ if (!IsAddressBreakPoint(bp.iAddress)) {
if (!IsAddressBreakPoint(bp.iAddress))
{
m_BreakPoints.push_back(bp); m_BreakPoints.push_back(bp);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
} }
} }
void BreakPoints::Add(u32 em_address, bool temp) void BreakPoints::Add(u32 em_address, bool temp) {
{
if (!IsAddressBreakPoint(em_address)) // only add new addresses if (!IsAddressBreakPoint(em_address)) // only add new addresses
{ {
TBreakPoint pt; // breakpoint settings TBreakPoint pt; // breakpoint settings
@ -73,22 +64,20 @@ void BreakPoints::Add(u32 em_address, bool temp)
m_BreakPoints.push_back(pt); m_BreakPoints.push_back(pt);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(em_address, 4); // jit->GetBlockCache()->InvalidateICache(em_address, 4);
} }
} }
void BreakPoints::Remove(u32 em_address) void BreakPoints::Remove(u32 em_address) {
{
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
if (it != m_BreakPoints.end()) if (it != m_BreakPoints.end())
m_BreakPoints.erase(it); m_BreakPoints.erase(it);
} }
void BreakPoints::Clear() void BreakPoints::Clear() {
{ // if (jit)
//if (jit)
//{ //{
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
// [](const TBreakPoint& bp) // [](const TBreakPoint& bp)

View file

@ -4,28 +4,27 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
class DebugInterface; class DebugInterface;
struct TBreakPoint struct TBreakPoint {
{ u32 iAddress;
u32 iAddress;
bool bOn; bool bOn;
bool bTemporary; bool bTemporary;
}; };
// Code breakpoints. // Code breakpoints.
class BreakPoints class BreakPoints {
{
public: public:
typedef std::vector<TBreakPoint> TBreakPoints; typedef std::vector<TBreakPoint> TBreakPoints;
typedef std::vector<std::string> TBreakPointsStr; typedef std::vector<std::string> TBreakPointsStr;
const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } const TBreakPoints& GetBreakPoints() {
return m_BreakPoints;
}
TBreakPointsStr GetStrings() const; TBreakPointsStr GetStrings() const;
void AddFromStrings(const TBreakPointsStr& bps); void AddFromStrings(const TBreakPointsStr& bps);
@ -35,7 +34,7 @@ public:
bool IsTempBreakPoint(u32 iAddress) const; bool IsTempBreakPoint(u32 iAddress) const;
// Add BreakPoint // Add BreakPoint
void Add(u32 em_address, bool temp=false); void Add(u32 em_address, bool temp = false);
void Add(const TBreakPoint& bp); void Add(const TBreakPoint& bp);
// Remove Breakpoint // Remove Breakpoint
@ -46,5 +45,5 @@ public:
private: private:
TBreakPoints m_BreakPoints; TBreakPoints m_BreakPoints;
u32 m_iBreakOnCount; u32 m_iBreakOnCount;
}; };

View file

@ -35,87 +35,90 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
template <class T> template <class T>
struct LinkedListItem : public T struct LinkedListItem : public T {
{ LinkedListItem<T>* next;
LinkedListItem<T> *next;
}; };
class PointerWrap; class PointerWrap;
class PointerWrapSection class PointerWrapSection {
{
public: public:
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { PointerWrapSection(PointerWrap& p, int ver, const char* title)
} : p_(p), ver_(ver), title_(title) {}
~PointerWrapSection(); ~PointerWrapSection();
bool operator == (const int &v) const { return ver_ == v; } bool operator==(const int& v) const {
bool operator != (const int &v) const { return ver_ != v; } return ver_ == v;
bool operator <= (const int &v) const { return ver_ <= v; } }
bool operator >= (const int &v) const { return ver_ >= v; } bool operator!=(const int& v) const {
bool operator < (const int &v) const { return ver_ < v; } return ver_ != v;
bool operator > (const int &v) const { return ver_ > v; } }
bool operator<=(const int& v) const {
return ver_ <= v;
}
bool operator>=(const int& v) const {
return ver_ >= v;
}
bool operator<(const int& v) const {
return ver_ < v;
}
bool operator>(const int& v) const {
return ver_ > v;
}
operator bool() const { operator bool() const {
return ver_ > 0; return ver_ > 0;
} }
private: private:
PointerWrap &p_; PointerWrap& p_;
int ver_; int ver_;
const char *title_; const char* title_;
}; };
// Wrapper class // Wrapper class
class PointerWrap class PointerWrap {
{ // This makes it a compile error if you forget to define DoState() on non-POD.
// This makes it a compile error if you forget to define DoState() on non-POD. // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
#ifdef _MSC_VER #ifdef _MSC_VER
template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = std::is_pod<T>::value,
bool isPointer = std::is_pointer<T>::value>
#else #else
template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value>
#endif #endif
struct DoHelper struct DoHelper {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count)
{
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
p->Do(x[i]); p->Do(x[i]);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{
p->DoClass(x); p->DoClass(x);
} }
}; };
template<typename T> template <typename T>
struct DoHelper<T, true, false> struct DoHelper<T, true, false> {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count) p->DoVoid((void*)x, sizeof(T) * count);
{
p->DoVoid((void *)x, sizeof(T) * count);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{ p->DoVoid((void*)&x, sizeof(x));
p->DoVoid((void *)&x, sizeof(x));
} }
}; };
public: public:
enum Mode { enum Mode {
MODE_READ = 1, // load MODE_READ = 1, // load
MODE_WRITE, // save MODE_WRITE, // save
MODE_MEASURE, // calculate size MODE_MEASURE, // calculate size
MODE_VERIFY, // compare MODE_VERIFY, // compare
}; };
enum Error { enum Error {
@ -124,247 +127,237 @@ public:
ERROR_FAILURE = 2, ERROR_FAILURE = 2,
}; };
u8 **ptr; u8** ptr;
Mode mode; Mode mode;
Error error; Error error;
public: public:
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {}
PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} PointerWrap(unsigned char** ptr_, int mode_)
: ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {}
PointerWrapSection Section(const char *title, int ver) { PointerWrapSection Section(const char* title, int ver) {
return Section(title, ver, ver); return Section(title, ver, ver);
} }
// The returned object can be compared against the version that was loaded. // The returned object can be compared against the version that was loaded.
// This can be used to support versions as old as minVer. // This can be used to support versions as old as minVer.
// Version = 0 means the section was not found. // Version = 0 means the section was not found.
PointerWrapSection Section(const char *title, int minVer, int ver) { PointerWrapSection Section(const char* title, int minVer, int ver) {
char marker[16] = {0}; char marker[16] = {0};
int foundVersion = ver; int foundVersion = ver;
strncpy(marker, title, sizeof(marker)); strncpy(marker, title, sizeof(marker));
if (!ExpectVoid(marker, sizeof(marker))) if (!ExpectVoid(marker, sizeof(marker))) {
{
// Might be before we added name markers for safety. // Might be before we added name markers for safety.
if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion)))
DoMarker(title); DoMarker(title);
// Wasn't found, but maybe we can still load the state. // Wasn't found, but maybe we can still load the state.
else else
foundVersion = 0; foundVersion = 0;
} } else
else
Do(foundVersion); Do(foundVersion);
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion,
title);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title); return PointerWrapSection(*this, -1, title);
} }
return PointerWrapSection(*this, foundVersion, title); return PointerWrapSection(*this, foundVersion, title);
} }
void SetMode(Mode mode_) {mode = mode_;} void SetMode(Mode mode_) {
Mode GetMode() const {return mode;} mode = mode_;
u8 **GetPPtr() {return ptr;} }
void SetError(Error error_) Mode GetMode() const {
{ return mode;
}
u8** GetPPtr() {
return ptr;
}
void SetError(Error error_) {
if (error < error_) if (error < error_)
error = error_; error = error_;
if (error > ERROR_WARNING) if (error > ERROR_WARNING)
mode = PointerWrap::MODE_MEASURE; mode = PointerWrap::MODE_MEASURE;
} }
bool ExpectVoid(void *data, int size) bool ExpectVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; if (memcmp(data, *ptr, size) != 0)
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything return false;
break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
return true; return true;
} }
void DoVoid(void *data, int size) void DoVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: memcpy(data, *ptr, size); break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; memcpy(data, *ptr, size);
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T *> &x) void Do(std::map<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T> &x) void Do(std::map<K, T>& x) {
{
T dv = T(); T dv = T();
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMap(std::map<K, T> &x, T &default_val) void DoMap(std::map<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{ x.clear();
x.clear(); while (number > 0) {
while (number > 0) K first = K();
{ Do(first);
K first = K(); T second = default_val;
Do(first); Do(second);
T second = default_val; x[first] = second;
Do(second); --number;
x[first] = second;
--number;
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::map<K, T>::iterator itr = x.begin();
typename std::map<K, T>::iterator itr = x.begin(); while (number > 0) {
while (number > 0) K first = itr->first;
{ Do(first);
K first = itr->first; Do(itr->second);
Do(first); --number;
Do(itr->second); ++itr;
--number;
++itr;
}
} }
break; } break;
} }
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T *> &x) void Do(std::multimap<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T> &x) void Do(std::multimap<K, T>& x) {
{
T dv = T(); T dv = T();
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMultimap(std::multimap<K, T> &x, T &default_val) void DoMultimap(std::multimap<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{ x.clear();
x.clear(); while (number > 0) {
while (number > 0) K first = K();
{ Do(first);
K first = K(); T second = default_val;
Do(first); Do(second);
T second = default_val; x.insert(std::make_pair(first, second));
Do(second); --number;
x.insert(std::make_pair(first, second));
--number;
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::multimap<K, T>::iterator itr = x.begin();
typename std::multimap<K, T>::iterator itr = x.begin(); while (number > 0) {
while (number > 0) Do(itr->first);
{ Do(itr->second);
Do(itr->first); --number;
Do(itr->second); ++itr;
--number;
++itr;
}
} }
break; } break;
} }
} }
// Store vectors. // Store vectors.
template<class T> template <class T>
void Do(std::vector<T *> &x) void Do(std::vector<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoVector(x, dv); DoVector(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x) void Do(std::vector<T>& x) {
{
T dv = T(); T dv = T();
DoVector(x, dv); DoVector(x, dv);
} }
template <class T>
template<class T> void DoPOD(std::vector<T>& x) {
void DoPOD(std::vector<T> &x)
{
T dv = T(); T dv = T();
DoVectorPOD(x, dv); DoVectorPOD(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x, T &default_val) void Do(std::vector<T>& x, T& default_val) {
{
DoVector(x, default_val); DoVector(x, default_val);
} }
template<class T> template <class T>
void DoVector(std::vector<T> &x, T &default_val) void DoVector(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -372,9 +365,8 @@ public:
DoArray(&x[0], vec_size); DoArray(&x[0], vec_size);
} }
template<class T> template <class T>
void DoVectorPOD(std::vector<T> &x, T &default_val) void DoVectorPOD(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -383,55 +375,48 @@ public:
} }
// Store deques. // Store deques.
template<class T> template <class T>
void Do(std::deque<T *> &x) void Do(std::deque<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void Do(std::deque<T> &x) void Do(std::deque<T>& x) {
{
T dv = T(); T dv = T();
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void DoDeque(std::deque<T> &x, T &default_val) void DoDeque(std::deque<T>& x, T& default_val) {
{
u32 deq_size = (u32)x.size(); u32 deq_size = (u32)x.size();
Do(deq_size); Do(deq_size);
x.resize(deq_size, default_val); x.resize(deq_size, default_val);
u32 i; u32 i;
for(i = 0; i < deq_size; i++) for (i = 0; i < deq_size; i++)
Do(x[i]); Do(x[i]);
} }
// Store STL lists. // Store STL lists.
template<class T> template <class T>
void Do(std::list<T *> &x) void Do(std::list<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
Do(x, dv); Do(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x) void Do(std::list<T>& x) {
{
T dv = T(); T dv = T();
DoList(x, dv); DoList(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x, T &default_val) void Do(std::list<T>& x, T& default_val) {
{
DoList(x, default_val); DoList(x, default_val);
} }
template<class T> template <class T>
void DoList(std::list<T> &x, T &default_val) void DoList(std::list<T>& x, T& default_val) {
{
u32 list_size = (u32)x.size(); u32 list_size = (u32)x.size();
Do(list_size); Do(list_size);
x.resize(list_size, default_val); x.resize(list_size, default_val);
@ -441,15 +426,11 @@ public:
Do(*itr); Do(*itr);
} }
// Store STL sets. // Store STL sets.
template <class T> template <class T>
void Do(std::set<T *> &x) void Do(std::set<T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (*it != nullptr) if (*it != nullptr)
delete *it; delete *it;
} }
@ -458,39 +439,31 @@ public:
} }
template <class T> template <class T>
void Do(std::set<T> &x) void Do(std::set<T>& x) {
{
DoSet(x); DoSet(x);
} }
template <class T> template <class T>
void DoSet(std::set<T> &x) void DoSet(std::set<T>& x) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) switch (mode) {
{ case MODE_READ: {
case MODE_READ: x.clear();
{ while (number-- > 0) {
x.clear(); T it = T();
while (number-- > 0) Do(it);
{ x.insert(it);
T it = T();
Do(it);
x.insert(it);
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::set<T>::iterator itr = x.begin();
typename std::set<T>::iterator itr = x.begin(); while (number-- > 0)
while (number-- > 0) Do(*itr++);
Do(*itr++); } break;
}
break;
default: default:
LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
@ -498,51 +471,58 @@ public:
} }
// Store strings. // Store strings.
void Do(std::string &x) void Do(std::string& x) {
{
int stringLen = (int)x.length() + 1; int stringLen = (int)x.length() + 1;
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (char*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (char*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (char*)*ptr), DEBUG_ASSERT_MSG((x == (char*)*ptr),
"Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
x.c_str(), (char*)*ptr, ptr); x.c_str(), (char*)*ptr, ptr);
break; break;
} }
(*ptr) += stringLen; (*ptr) += stringLen;
} }
void Do(std::wstring &x) void Do(std::wstring& x) {
{ int stringLen = sizeof(wchar_t) * ((int)x.length() + 1);
int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (wchar_t*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (wchar_t*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr),
"Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
x.c_str(), (wchar_t*)*ptr, ptr); x.c_str(), (wchar_t*)*ptr, ptr);
break; break;
} }
(*ptr) += stringLen; (*ptr) += stringLen;
} }
template<class T> template <class T>
void DoClass(T &x) { void DoClass(T& x) {
x.DoState(*this); x.DoState(*this);
} }
template<class T> template <class T>
void DoClass(T *&x) { void DoClass(T*& x) {
if (mode == MODE_READ) if (mode == MODE_READ) {
{
if (x != nullptr) if (x != nullptr)
delete x; delete x;
x = new T(); x = new T();
@ -550,81 +530,70 @@ public:
x->DoState(*this); x->DoState(*this);
} }
template<class T> template <class T>
void DoArray(T *x, int count) { void DoArray(T* x, int count) {
DoHelper<T>::DoArray(this, x, count); DoHelper<T>::DoArray(this, x, count);
} }
template<class T> template <class T>
void Do(T &x) { void Do(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPOD(T &x) { void DoPOD(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPointer(T* &x, T*const base) { void DoPointer(T*& x, T* const base) {
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range // pointers can be more than 2^31 apart, but you're using this function wrong if you need
// that much range
s32 offset = x - base; s32 offset = x - base;
Do(offset); Do(offset);
if (mode == MODE_READ) if (mode == MODE_READ)
x = base + offset; x = base + offset;
} }
template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) void (*TDo)(PointerWrap&, T*)>
{ void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) {
LinkedListItem<T>* list_cur = list_start; LinkedListItem<T>* list_cur = list_start;
LinkedListItem<T>* prev = nullptr; LinkedListItem<T>* prev = nullptr;
while (true) while (true) {
{
u8 shouldExist = (list_cur ? 1 : 0); u8 shouldExist = (list_cur ? 1 : 0);
Do(shouldExist); Do(shouldExist);
if (shouldExist == 1) if (shouldExist == 1) {
{
LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
TDo(*this, (T*)cur); TDo(*this, (T*)cur);
if (!list_cur) if (!list_cur) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ)
{
cur->next = nullptr; cur->next = nullptr;
list_cur = cur; list_cur = cur;
if (prev) if (prev)
prev->next = cur; prev->next = cur;
else else
list_start = cur; list_start = cur;
} } else {
else
{
TFree(cur); TFree(cur);
continue; continue;
} }
} }
} } else {
else if (mode == MODE_READ) {
{
if (mode == MODE_READ)
{
if (prev) if (prev)
prev->next = nullptr; prev->next = nullptr;
if (list_end) if (list_end)
*list_end = prev; *list_end = prev;
if (list_cur) if (list_cur) {
{
if (list_start == list_cur) if (list_start == list_cur)
list_start = nullptr; list_start = nullptr;
do do {
{
LinkedListItem<T>* next = list_cur->next; LinkedListItem<T>* next = list_cur->next;
TFree(list_cur); TFree(list_cur);
list_cur = next; list_cur = next;
} } while (list_cur);
while (list_cur);
} }
} }
break; break;
@ -634,13 +603,13 @@ public:
} }
} }
void DoMarker(const char* prevName, u32 arbitraryNumber=0x42) void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) {
{
u32 cookie = arbitraryNumber; u32 cookie = arbitraryNumber;
Do(cookie); Do(cookie);
if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) {
{ LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); "Aborting savestate load...",
prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
} }
} }

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/memory_util.h" #include "common/memory_util.h"
@ -14,24 +13,27 @@
// having to prefix them with gen-> or something similar. // having to prefix them with gen-> or something similar.
// Example implementation: // Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {} // class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T, NonCopyable template <class T>
{ class CodeBlock : public T, NonCopyable {
private: private:
// A privately used function to set the executable RAM space to something invalid. // A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
// instruction
virtual void PoisonMemory() = 0; virtual void PoisonMemory() = 0;
protected: protected:
u8 *region; u8* region;
size_t region_size; size_t region_size;
public: public:
CodeBlock() : region(nullptr), region_size(0) {} CodeBlock() : region(nullptr), region_size(0) {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); } virtual ~CodeBlock() {
if (region)
FreeCodeSpace();
}
// Call this before you generate any code. // Call this before you generate any code.
void AllocCodeSpace(int size) void AllocCodeSpace(int size) {
{
region_size = size; region_size = size;
region = (u8*)AllocateExecutableMemory(region_size); region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePtr(region); T::SetCodePtr(region);
@ -39,15 +41,13 @@ public:
// Always clear code space with breakpoints, so that if someone accidentally executes // Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger. // uninitialized, it just breaks into the debugger.
void ClearCodeSpace() void ClearCodeSpace() {
{
PoisonMemory(); PoisonMemory();
ResetCodePtr(); ResetCodePtr();
} }
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() void FreeCodeSpace() {
{
#ifdef __SYMBIAN32__ #ifdef __SYMBIAN32__
ResetExecutableMemory(region); ResetExecutableMemory(region);
#else #else
@ -57,33 +57,29 @@ public:
region_size = 0; region_size = 0;
} }
bool IsInSpace(const u8 *ptr) bool IsInSpace(const u8* ptr) {
{
return (ptr >= region) && (ptr < (region + region_size)); return (ptr >= region) && (ptr < (region + region_size));
} }
// Cannot currently be undone. Will write protect the entire code region. // Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect() void WriteProtect() {
{
WriteProtectMemory(region, region_size, true); WriteProtectMemory(region, region_size, true);
} }
void ResetCodePtr() void ResetCodePtr() {
{
T::SetCodePtr(region); T::SetCodePtr(region);
} }
size_t GetSpaceLeft() const size_t GetSpaceLeft() const {
{
return region_size - (T::GetCodePtr() - region); return region_size - (T::GetCodePtr() - region);
} }
u8 *GetBasePtr() { u8* GetBasePtr() {
return region; return region;
} }
size_t GetOffset(const u8 *ptr) const { size_t GetOffset(const u8* ptr) const {
return ptr - region; return ptr - region;
} }
}; };

View file

@ -56,7 +56,7 @@ constexpr u8 Convert8To6(u8 value) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return { bytes[3], bytes[2], bytes[1], bytes[0] }; return {bytes[3], bytes[2], bytes[1], bytes[0]};
} }
/** /**
@ -65,7 +65,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return { bytes[2], bytes[1], bytes[0], 255 }; return {bytes[2], bytes[1], bytes[0], 255};
} }
/** /**
@ -74,7 +74,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return { bytes[1], bytes[0], 0, 255 }; return {bytes[1], bytes[0], 0, 255};
} }
/** /**
@ -84,8 +84,8 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255 }; Convert5To8(pixel & 0x1F), 255};
} }
/** /**
@ -95,8 +95,8 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) }; Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
} }
/** /**
@ -106,8 +106,8 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) }; Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
} }
/** /**
@ -134,7 +134,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @return Resulting values stored as a Math::Vec2 * @return Resulting values stored as a Math::Vec2
*/ */
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] }; return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
} }
/** /**
@ -175,8 +175,8 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color * @param bytes Destination pointer to store encoded color
*/ */
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) =
(Convert8To6(color.g()) << 5) | Convert8To5(color.b()); (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
} }
/** /**
@ -186,7 +186,8 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
(Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
} }
/** /**
@ -196,7 +197,8 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
(Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
} }
/** /**

View file

@ -7,14 +7,13 @@
#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
#include <cstdlib> // for exit #include <cstdlib> // for exit
#endif #endif
#include "common_types.h" #include "common_types.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
#define CONCAT2(x, y) DO_CONCAT2(x, y) #define CONCAT2(x, y) DO_CONCAT2(x, y)
#define DO_CONCAT2(x, y) x ## y #define DO_CONCAT2(x, y) x##y
// helper macro to properly align structure members. // helper macro to properly align structure members.
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121", // Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
@ -24,9 +23,9 @@
// Inlining // Inlining
#ifdef _WIN32 #ifdef _WIN32
#define FORCE_INLINE __forceinline #define FORCE_INLINE __forceinline
#else #else
#define FORCE_INLINE inline __attribute__((always_inline)) #define FORCE_INLINE inline __attribute__((always_inline))
#endif #endif
#ifndef _MSC_VER #ifndef _MSC_VER
@ -46,7 +45,8 @@
#else #else
inline u32 rotl(u32 x, int shift) { inline u32 rotl(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x << shift) | (x >> (32 - shift)); return (x << shift) | (x >> (32 - shift));
} }
#endif #endif
@ -56,17 +56,18 @@ inline u32 rotl(u32 x, int shift) {
#else #else
inline u32 rotr(u32 x, int shift) { inline u32 rotr(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x >> shift) | (x << (32 - shift)); return (x >> shift) | (x << (32 - shift));
} }
#endif #endif
inline u64 _rotl64(u64 x, unsigned int shift){ inline u64 _rotl64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x << n) | (x >> (64 - n)); return (x << n) | (x >> (64 - n));
} }
inline u64 _rotr64(u64 x, unsigned int shift){ inline u64 _rotr64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x >> n) | (x << (64 - n)); return (x >> n) | (x << (64 - n));
} }
@ -74,17 +75,17 @@ inline u64 _rotr64(u64 x, unsigned int shift){
#else // _MSC_VER #else // _MSC_VER
#if (_MSC_VER < 1900) #if (_MSC_VER < 1900)
// Function Cross-Compatibility // Function Cross-Compatibility
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
// Locale Cross-Compatibility // Locale Cross-Compatibility
#define locale_t _locale_t #define locale_t _locale_t
extern "C" { extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void); __declspec(dllimport) void __stdcall DebugBreak(void);
} }
#define Crash() {DebugBreak();} #define Crash() DebugBreak()
// cstdlib provides these on MSVC // cstdlib provides these on MSVC
#define rotr _rotr #define rotr _rotr

View file

@ -16,13 +16,13 @@
#define ROOT_DIR "." #define ROOT_DIR "."
#define USERDATA_DIR "user" #define USERDATA_DIR "user"
#ifdef USER_DIR #ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR #define EMU_DATA_DIR USER_DIR
#else #else
#ifdef _WIN32 #ifdef _WIN32
#define EMU_DATA_DIR "Citra Emulator" #define EMU_DATA_DIR "Citra Emulator"
#else #else
#define EMU_DATA_DIR "citra-emu" #define EMU_DATA_DIR "citra-emu"
#endif #endif
#endif #endif
// Dirs in both User and Sys // Dirs in both User and Sys
@ -31,32 +31,32 @@
#define JAP_DIR "JAP" #define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX) // Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
#define CONFIG_DIR "config" #define CONFIG_DIR "config"
#define GAMECONFIG_DIR "game_config" #define GAMECONFIG_DIR "game_config"
#define MAPS_DIR "maps" #define MAPS_DIR "maps"
#define CACHE_DIR "cache" #define CACHE_DIR "cache"
#define SDMC_DIR "sdmc" #define SDMC_DIR "sdmc"
#define NAND_DIR "nand" #define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata" #define SYSDATA_DIR "sysdata"
#define SHADERCACHE_DIR "shader_cache" #define SHADERCACHE_DIR "shader_cache"
#define STATESAVES_DIR "state_saves" #define STATESAVES_DIR "state_saves"
#define SCREENSHOTS_DIR "screenShots" #define SCREENSHOTS_DIR "screenShots"
#define DUMP_DIR "dump" #define DUMP_DIR "dump"
#define DUMP_TEXTURES_DIR "textures" #define DUMP_TEXTURES_DIR "textures"
#define DUMP_FRAMES_DIR "frames" #define DUMP_FRAMES_DIR "frames"
#define DUMP_AUDIO_DIR "audio" #define DUMP_AUDIO_DIR "audio"
#define LOGS_DIR "logs" #define LOGS_DIR "logs"
#define SHADERS_DIR "shaders" #define SHADERS_DIR "shaders"
#define SYSCONF_DIR "sysconf" #define SYSCONF_DIR "sysconf"
// Filenames // Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX) // Files in the directory returned by GetUserPath(D_CONFIG_IDX)
#define EMU_CONFIG "emu.ini" #define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini" #define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini" #define LOGGER_CONFIG "logger.ini"
// Sys files // Sys files
#define SHARED_FONT "shared_font.bin" #define SHARED_FONT "shared_font.bin"
// Files in the directory returned by GetUserPath(D_LOGS_IDX) // Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "emu.log" #define MAIN_LOG "emu.log"

View file

@ -32,18 +32,18 @@
#endif #endif
#endif #endif
typedef std::uint8_t u8; ///< 8-bit unsigned byte typedef std::uint8_t u8; ///< 8-bit unsigned byte
typedef std::uint16_t u16; ///< 16-bit unsigned short typedef std::uint16_t u16; ///< 16-bit unsigned short
typedef std::uint32_t u32; ///< 32-bit unsigned word typedef std::uint32_t u32; ///< 32-bit unsigned word
typedef std::uint64_t u64; ///< 64-bit unsigned int typedef std::uint64_t u64; ///< 64-bit unsigned int
typedef std::int8_t s8; ///< 8-bit signed byte typedef std::int8_t s8; ///< 8-bit signed byte
typedef std::int16_t s16; ///< 16-bit signed short typedef std::int16_t s16; ///< 16-bit signed short
typedef std::int32_t s32; ///< 32-bit signed word typedef std::int32_t s32; ///< 32-bit signed word
typedef std::int64_t s64; ///< 64-bit signed int typedef std::int64_t s64; ///< 64-bit signed int
typedef float f32; ///< 32-bit floating point typedef float f32; ///< 32-bit floating point
typedef double f64; ///< 64-bit floating point typedef double f64; ///< 64-bit floating point
// TODO: It would be nice to eventually replace these with strong types that prevent accidental // TODO: It would be nice to eventually replace these with strong types that prevent accidental
// conversion between each other. // conversion between each other.

Some files were not shown because too many files have changed in this diff Show more