[LINT] Linted files + Added lint job to CI
This commit is contained in:
parent
e8afad8f8a
commit
b9061e6292
|
@ -44,37 +44,51 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check Clang-Format Version
|
||||||
|
run: clang-format --version
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: .\xb lint --all
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build (Windows) # runner.os can't be used here
|
name: Build (Windows) # runner.os can't be used here
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
needs: lint
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
run: .\xb setup
|
run: .\xb setup
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: .\xb build --config=Release --target=src\xenia-app --target=src\xenia-vfs-dump
|
run: .\xb build --config=Release --target=src\xenia-app
|
||||||
|
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
run: |
|
run: |
|
||||||
robocopy . build\bin\${{ runner.os }}\Release LICENSE /r:0 /w:0
|
robocopy . build\bin\${{ runner.os }}\Release LICENSE /r:0 /w:0
|
||||||
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0
|
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0
|
||||||
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia-vfs-dump xenia-vfs-dump.exe xenia-vfs-dump.pdb LICENSE /r:0 /w:0
|
|
||||||
If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 }
|
If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 }
|
||||||
- name: Upload xenia-vfs-dump artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: xenia-vfs-dump_canary
|
|
||||||
path: artifacts\xenia-vfs-dump
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Upload xenia canary artifacts
|
- name: Upload xenia canary artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: xenia_canary
|
name: xenia_canary
|
||||||
path: artifacts\xenia_canary
|
path: artifacts\xenia_canary
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
if: |
|
if: |
|
||||||
github.repository == 'xenia-canary/xenia-canary' &&
|
github.repository == 'xenia-canary/xenia-canary' &&
|
||||||
|
|
|
@ -35,9 +35,10 @@
|
||||||
// and let the normal AudioSystem handling take it, to prevent duplicate
|
// and let the normal AudioSystem handling take it, to prevent duplicate
|
||||||
// implementations. They can be found in xboxkrnl_audio_xma.cc
|
// implementations. They can be found in xboxkrnl_audio_xma.cc
|
||||||
|
|
||||||
DEFINE_uint32(
|
DEFINE_uint32(apu_max_queued_frames, 64,
|
||||||
apu_max_queued_frames, 64,
|
"Allows changing max buffered audio frames to reduce audio "
|
||||||
"Allows changing max buffered audio frames to reduce audio delay. Minimum is 16.", "APU");
|
"delay. Minimum is 16.",
|
||||||
|
"APU");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace apu {
|
namespace apu {
|
||||||
|
@ -76,11 +77,14 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
worker_running_ = true;
|
worker_running_ = true;
|
||||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
worker_thread_ =
|
||||||
new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() {
|
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||||
|
kernel_state, 128 * 1024, 0,
|
||||||
|
[this]() {
|
||||||
WorkerThreadMain();
|
WorkerThreadMain();
|
||||||
return 0;
|
return 0;
|
||||||
}, kernel_state->GetSystemProcess()));
|
},
|
||||||
|
kernel_state->GetSystemProcess()));
|
||||||
// As we run audio callbacks the debugger must be able to suspend us.
|
// As we run audio callbacks the debugger must be able to suspend us.
|
||||||
worker_thread_->set_can_debugger_suspend(true);
|
worker_thread_->set_can_debugger_suspend(true);
|
||||||
worker_thread_->set_name("Audio Worker");
|
worker_thread_->set_name("Audio Worker");
|
||||||
|
|
|
@ -21,7 +21,6 @@ namespace conversion {
|
||||||
|
|
||||||
#if XE_ARCH_AMD64
|
#if XE_ARCH_AMD64
|
||||||
|
|
||||||
|
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
static void _generic_sequential_6_BE_to_interleaved_6_LE(
|
static void _generic_sequential_6_BE_to_interleaved_6_LE(
|
||||||
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
#include "xenia/base/string_buffer.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/cpu/thread_state.h"
|
#include "xenia/cpu/thread_state.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
#include "xenia/kernel/xthread.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "third_party/FFmpeg/libavutil/log.h"
|
#include "third_party/FFmpeg/libavutil/log.h"
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -102,8 +102,7 @@ void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
|
||||||
StringBuffer buff;
|
StringBuffer buff;
|
||||||
buff.AppendVarargs(fmt, va);
|
buff.AppendVarargs(fmt, va);
|
||||||
xe::logging::AppendLogLineFormat(LogSrc::Apu, log_level, level_char,
|
xe::logging::AppendLogLineFormat(LogSrc::Apu, log_level, level_char,
|
||||||
"ffmpeg: {}",
|
"ffmpeg: {}", buff.to_string_view());
|
||||||
buff.to_string_view());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
||||||
|
@ -141,11 +140,16 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
||||||
worker_running_ = true;
|
worker_running_ = true;
|
||||||
work_event_ = xe::threading::Event::CreateAutoResetEvent(false);
|
work_event_ = xe::threading::Event::CreateAutoResetEvent(false);
|
||||||
assert_not_null(work_event_);
|
assert_not_null(work_event_);
|
||||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
worker_thread_ =
|
||||||
new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() {
|
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||||
|
kernel_state, 128 * 1024, 0,
|
||||||
|
[this]() {
|
||||||
WorkerThreadMain();
|
WorkerThreadMain();
|
||||||
return 0;
|
return 0;
|
||||||
}, kernel_state->GetIdleProcess()));//this one doesnt need any process actually. never calls any guest code
|
},
|
||||||
|
kernel_state
|
||||||
|
->GetIdleProcess())); // this one doesnt need any process
|
||||||
|
// actually. never calls any guest code
|
||||||
worker_thread_->set_name("XMA Decoder");
|
worker_thread_->set_name("XMA Decoder");
|
||||||
worker_thread_->set_can_debugger_suspend(true);
|
worker_thread_->set_can_debugger_suspend(true);
|
||||||
worker_thread_->Create();
|
worker_thread_->Create();
|
||||||
|
|
|
@ -39,8 +39,8 @@ class Clock {
|
||||||
// Host tick count. Generally QueryHostTickCount() should be used.
|
// Host tick count. Generally QueryHostTickCount() should be used.
|
||||||
static uint64_t host_tick_count_platform();
|
static uint64_t host_tick_count_platform();
|
||||||
#if XE_CLOCK_RAW_AVAILABLE
|
#if XE_CLOCK_RAW_AVAILABLE
|
||||||
//chrispy: the way msvc was ordering the branches was causing rdtsc to be speculatively executed each time
|
// chrispy: the way msvc was ordering the branches was causing rdtsc to be
|
||||||
//the branch history was lost
|
// speculatively executed each time the branch history was lost
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
static uint64_t host_tick_count_raw();
|
static uint64_t host_tick_count_raw();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,9 +41,6 @@
|
||||||
"\n" \
|
"\n" \
|
||||||
"Set the cvar 'clock_source_raw' to 'false'.");
|
"Set the cvar 'clock_source_raw' to 'false'.");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
// Getting the TSC frequency can be a bit tricky. This method here only works on
|
// Getting the TSC frequency can be a bit tricky. This method here only works on
|
||||||
// Intel as it seems. There is no easy way to get the frequency outside of ring0
|
// Intel as it seems. There is no easy way to get the frequency outside of ring0
|
||||||
|
@ -75,8 +72,6 @@ uint64_t Clock::host_tick_frequency_raw() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (max_cpuid >= 0x15) {
|
if (max_cpuid >= 0x15) {
|
||||||
// 15H Get TSC/Crystal ratio and Crystal Hz.
|
// 15H Get TSC/Crystal ratio and Crystal Hz.
|
||||||
xe_cpu_cpuid(0x15, eax, ebx, ecx, edx);
|
xe_cpu_cpuid(0x15, eax, ebx, ecx, edx);
|
||||||
|
@ -98,7 +93,6 @@ uint64_t Clock::host_tick_frequency_raw() {
|
||||||
return cpu_base_freq;
|
return cpu_base_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CLOCK_FATAL("The clock frequency could not be determined.");
|
CLOCK_FATAL("The clock frequency could not be determined.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ static bool has_shell_environment_variable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachConsole() {
|
void AttachConsole() {
|
||||||
|
|
||||||
bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
|
bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
|
||||||
#if 0
|
#if 0
|
||||||
if (!has_console || !has_shell_environment_variable()) {
|
if (!has_console || !has_shell_environment_variable()) {
|
||||||
|
|
|
@ -172,8 +172,7 @@ CommandVar<T>::CommandVar(const char* name, T* default_value,
|
||||||
default_value_(*default_value),
|
default_value_(*default_value),
|
||||||
current_value_(default_value),
|
current_value_(default_value),
|
||||||
commandline_value_(),
|
commandline_value_(),
|
||||||
description_(description)
|
description_(description) {}
|
||||||
{}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
ConfigVar<T>::ConfigVar(const char* name, T* default_value,
|
ConfigVar<T>::ConfigVar(const char* name, T* default_value,
|
||||||
|
|
|
@ -606,7 +606,8 @@ union IDivExtraInfo {
|
||||||
} info;
|
} info;
|
||||||
};
|
};
|
||||||
// returns magicnum multiplier
|
// returns magicnum multiplier
|
||||||
static constexpr uint32_t PregenerateUint32Div(uint32_t _denom, uint32_t& out_extra) {
|
static constexpr uint32_t PregenerateUint32Div(uint32_t _denom,
|
||||||
|
uint32_t& out_extra) {
|
||||||
IDivExtraInfo extra{};
|
IDivExtraInfo extra{};
|
||||||
|
|
||||||
uint32_t d = _denom;
|
uint32_t d = _denom;
|
||||||
|
@ -662,7 +663,8 @@ static constexpr uint32_t ApplyUint32Div(uint32_t num, uint32_t mul,
|
||||||
|
|
||||||
extra.value_ = extradata;
|
extra.value_ = extradata;
|
||||||
|
|
||||||
uint32_t result = static_cast<uint32_t>((static_cast<uint64_t>(num) * static_cast<uint64_t>(mul)) >> 32);
|
uint32_t result = static_cast<uint32_t>(
|
||||||
|
(static_cast<uint64_t>(num) * static_cast<uint64_t>(mul)) >> 32);
|
||||||
if (extra.info.add_) {
|
if (extra.info.add_) {
|
||||||
uint32_t addend = result + num;
|
uint32_t addend = result + num;
|
||||||
addend = ((addend < result ? 0x80000000 : 0) | addend);
|
addend = ((addend < result ? 0x80000000 : 0) | addend);
|
||||||
|
@ -672,7 +674,8 @@ static constexpr uint32_t ApplyUint32Div(uint32_t num, uint32_t mul,
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr uint32_t ApplyUint32UMod(uint32_t num, uint32_t mul,
|
static constexpr uint32_t ApplyUint32UMod(uint32_t num, uint32_t mul,
|
||||||
uint32_t extradata, uint32_t original) {
|
uint32_t extradata,
|
||||||
|
uint32_t original) {
|
||||||
uint32_t dived = ApplyUint32Div(num, mul, extradata);
|
uint32_t dived = ApplyUint32Div(num, mul, extradata);
|
||||||
unsigned result = num - (dived * original);
|
unsigned result = num - (dived * original);
|
||||||
|
|
||||||
|
@ -701,8 +704,7 @@ struct MagicDiv {
|
||||||
return extra.info.shift_;
|
return extra.info.shift_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint32_t GetMultiplier() const { return multiplier_;
|
constexpr uint32_t GetMultiplier() const { return multiplier_; }
|
||||||
}
|
|
||||||
constexpr uint32_t Apply(uint32_t numerator) const {
|
constexpr uint32_t Apply(uint32_t numerator) const {
|
||||||
return ApplyUint32Div(numerator, multiplier_, extradata_);
|
return ApplyUint32Div(numerator, multiplier_, extradata_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,8 @@ static void vastcpy_impl_repmovs(CacheLine* XE_RESTRICT physaddr,
|
||||||
__movsq((unsigned long long*)physaddr, (unsigned long long*)rdmapping,
|
__movsq((unsigned long long*)physaddr, (unsigned long long*)rdmapping,
|
||||||
written_length / 8);
|
written_length / 8);
|
||||||
#else
|
#else
|
||||||
memcpy((unsigned char*)physaddr, (const unsigned char*)rdmapping, written_length);
|
memcpy((unsigned char*)physaddr, (const unsigned char*)rdmapping,
|
||||||
|
written_length);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
XE_COLD
|
XE_COLD
|
||||||
|
@ -331,9 +332,9 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
|
||||||
|
|
||||||
__m256i output1 = _mm256_shuffle_epi8(input1, shufmask);
|
__m256i output1 = _mm256_shuffle_epi8(input1, shufmask);
|
||||||
__m256i output2 = _mm256_shuffle_epi8(input2, shufmask);
|
__m256i output2 = _mm256_shuffle_epi8(input2, shufmask);
|
||||||
//chrispy: todo, benchmark this w/ and w/out these prefetches here on multiple machines
|
// chrispy: todo, benchmark this w/ and w/out these prefetches here on multiple
|
||||||
//finding a good distance for prefetchw in particular is probably important
|
// machines finding a good distance for prefetchw in particular is probably
|
||||||
//for when we're writing across 2 cachelines
|
// important for when we're writing across 2 cachelines
|
||||||
#if 0
|
#if 0
|
||||||
if (i + 48 <= count) {
|
if (i + 48 <= count) {
|
||||||
swcache::PrefetchNTA(&src[i + 32]);
|
swcache::PrefetchNTA(&src[i + 32]);
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
|
||||||
#include "xenia/base/byte_order.h"
|
#include "xenia/base/byte_order.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
#ifndef XENIA_BASE_MUTEX_H_
|
#ifndef XENIA_BASE_MUTEX_H_
|
||||||
#define XENIA_BASE_MUTEX_H_
|
#define XENIA_BASE_MUTEX_H_
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "platform.h"
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "platform.h"
|
||||||
#define XE_ENABLE_FAST_WIN32_MUTEX 1
|
#define XE_ENABLE_FAST_WIN32_MUTEX 1
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,6 @@ class RingBuffer {
|
||||||
ring_size_t offset_delta = write_offs - read_offs;
|
ring_size_t offset_delta = write_offs - read_offs;
|
||||||
ring_size_t wrap_read_count = (cap - read_offs) + write_offs;
|
ring_size_t wrap_read_count = (cap - read_offs) + write_offs;
|
||||||
|
|
||||||
|
|
||||||
if (XE_LIKELY(read_offs <= write_offs)) {
|
if (XE_LIKELY(read_offs <= write_offs)) {
|
||||||
return offset_delta; // will be 0 if they are equal, semantically
|
return offset_delta; // will be 0 if they are equal, semantically
|
||||||
// identical to old code (i checked the asm, msvc
|
// identical to old code (i checked the asm, msvc
|
||||||
|
|
|
@ -34,7 +34,6 @@ struct SimpleFreelist {
|
||||||
node->next_ = head_;
|
node->next_ = head_;
|
||||||
head_ = node;
|
head_ = node;
|
||||||
}
|
}
|
||||||
void Reset() { head_ = nullptr;
|
void Reset() { head_ = nullptr; }
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -33,7 +33,9 @@ using WaitItem = TimerQueueWaitItem;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
edit: actually had to change it back, when i was testing it only worked because i fixed disruptorplus' code to compile (it gives wrong args to condition_variable::wait_until) but now builds
|
edit: actually had to change it back, when i was testing it only worked
|
||||||
|
because i fixed disruptorplus' code to compile (it gives wrong args to
|
||||||
|
condition_variable::wait_until) but now builds
|
||||||
|
|
||||||
*/
|
*/
|
||||||
using WaitStrat = dp::blocking_wait_strategy;
|
using WaitStrat = dp::blocking_wait_strategy;
|
||||||
|
|
|
@ -78,7 +78,8 @@ class Backend {
|
||||||
virtual void InitializeBackendContext(void* ctx) {}
|
virtual void InitializeBackendContext(void* ctx) {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Free any dynamically allocated data/resources that the backendcontext uses
|
Free any dynamically allocated data/resources that the backendcontext
|
||||||
|
uses
|
||||||
*/
|
*/
|
||||||
virtual void DeinitializeBackendContext(void* ctx) {}
|
virtual void DeinitializeBackendContext(void* ctx) {}
|
||||||
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode){};
|
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode){};
|
||||||
|
|
|
@ -314,7 +314,6 @@ SIMPLE_THREEOPERAND(vpshaw, xop_VPSHAW)
|
||||||
SIMPLE_THREEOPERAND(vpshad, xop_VPSHAD)
|
SIMPLE_THREEOPERAND(vpshad, xop_VPSHAD)
|
||||||
SIMPLE_THREEOPERAND(vpshaq, xop_VPSHAQ)
|
SIMPLE_THREEOPERAND(vpshaq, xop_VPSHAQ)
|
||||||
|
|
||||||
|
|
||||||
SIMPLE_THREEOPERAND(vpshlb, xop_VPSHLB)
|
SIMPLE_THREEOPERAND(vpshlb, xop_VPSHLB)
|
||||||
SIMPLE_THREEOPERAND(vpshlw, xop_VPSHLW)
|
SIMPLE_THREEOPERAND(vpshlw, xop_VPSHLW)
|
||||||
SIMPLE_THREEOPERAND(vpshld, xop_VPSHLD)
|
SIMPLE_THREEOPERAND(vpshld, xop_VPSHLD)
|
||||||
|
|
|
@ -924,7 +924,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
||||||
Xbyak::Label L18, L2, L35, L4, L9, L8, L10, L11, L12, L13, L1;
|
Xbyak::Label L18, L2, L35, L4, L9, L8, L10, L11, L12, L13, L1;
|
||||||
Xbyak::Label LC1, _LCPI3_1;
|
Xbyak::Label LC1, _LCPI3_1;
|
||||||
Xbyak::Label handle_denormal_input;
|
Xbyak::Label handle_denormal_input;
|
||||||
Xbyak::Label specialcheck_1, convert_to_signed_inf_and_ret, handle_oddball_denormal;
|
Xbyak::Label specialcheck_1, convert_to_signed_inf_and_ret,
|
||||||
|
handle_oddball_denormal;
|
||||||
|
|
||||||
auto emulate_lzcnt_helper_unary_reg = [this](auto& reg, auto& scratch_reg) {
|
auto emulate_lzcnt_helper_unary_reg = [this](auto& reg, auto& scratch_reg) {
|
||||||
inLocalLabel();
|
inLocalLabel();
|
||||||
|
@ -1107,7 +1108,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
||||||
xchg(ecx, edx);
|
xchg(ecx, edx);
|
||||||
// esi is just the value of xmm0's low word, so we can restore it from there
|
// esi is just the value of xmm0's low word, so we can restore it from there
|
||||||
shl(r8d, cl);
|
shl(r8d, cl);
|
||||||
mov(ecx, edx); // restore ecx, dont xchg because we're going to spoil edx anyway
|
mov(ecx,
|
||||||
|
edx); // restore ecx, dont xchg because we're going to spoil edx anyway
|
||||||
mov(edx, r8d);
|
mov(edx, r8d);
|
||||||
vmovd(r8d, xmm0);
|
vmovd(r8d, xmm0);
|
||||||
}
|
}
|
||||||
|
@ -1131,7 +1133,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
||||||
dd(0xFF800000);
|
dd(0xFF800000);
|
||||||
dd(0x7F800000);
|
dd(0x7F800000);
|
||||||
L(LC1);
|
L(LC1);
|
||||||
//the position of 7FC00000 here matters, this address will be indexed in handle_oddball_denormal
|
// the position of 7FC00000 here matters, this address will be indexed in
|
||||||
|
// handle_oddball_denormal
|
||||||
dd(0x7FC00000);
|
dd(0x7FC00000);
|
||||||
dd(0x5F34FD00);
|
dd(0x5F34FD00);
|
||||||
|
|
||||||
|
@ -1148,11 +1151,13 @@ void* X64HelperEmitter::EmitVectorVRsqrteHelper(void* scalar_helper) {
|
||||||
Xbyak::Label check_scalar_operation_in_vmx, actual_vector_version;
|
Xbyak::Label check_scalar_operation_in_vmx, actual_vector_version;
|
||||||
auto result_ptr =
|
auto result_ptr =
|
||||||
GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_xmms[0]));
|
GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_xmms[0]));
|
||||||
auto counter_ptr = GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_u64s[2]));
|
auto counter_ptr =
|
||||||
|
GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_u64s[2]));
|
||||||
counter_ptr.setBit(64);
|
counter_ptr.setBit(64);
|
||||||
|
|
||||||
// shuffle and xor to check whether all lanes are equal
|
// shuffle and xor to check whether all lanes are equal
|
||||||
//sadly has to leave the float pipeline for the vptest, which is moderate yikes
|
// sadly has to leave the float pipeline for the vptest, which is moderate
|
||||||
|
// yikes
|
||||||
vmovhlps(xmm2, xmm0, xmm0);
|
vmovhlps(xmm2, xmm0, xmm0);
|
||||||
vmovsldup(xmm1, xmm0);
|
vmovsldup(xmm1, xmm0);
|
||||||
vxorps(xmm1, xmm1, xmm0);
|
vxorps(xmm1, xmm1, xmm0);
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "xenia/base/cvar.h"
|
|
||||||
#include "xenia/base/bit_map.h"
|
#include "xenia/base/bit_map.h"
|
||||||
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/cpu/backend/backend.h"
|
#include "xenia/cpu/backend/backend.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32 == 1
|
#if XE_PLATFORM_WIN32 == 1
|
||||||
|
@ -45,8 +45,9 @@ typedef void (*ResolveFunctionThunk)();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
place guest trampolines in the memory range that the HV normally occupies.
|
place guest trampolines in the memory range that the HV normally occupies.
|
||||||
This way guests can call in via the indirection table and we don't have to clobber/reuse an existing memory range
|
This way guests can call in via the indirection table and we don't have to
|
||||||
The xboxkrnl range is already used by export trampolines (see kernel/kernel_module.cc)
|
clobber/reuse an existing memory range The xboxkrnl range is already used by
|
||||||
|
export trampolines (see kernel/kernel_module.cc)
|
||||||
*/
|
*/
|
||||||
static constexpr uint32_t GUEST_TRAMPOLINE_BASE = 0x80000000;
|
static constexpr uint32_t GUEST_TRAMPOLINE_BASE = 0x80000000;
|
||||||
static constexpr uint32_t GUEST_TRAMPOLINE_END = 0x80040000;
|
static constexpr uint32_t GUEST_TRAMPOLINE_END = 0x80040000;
|
||||||
|
@ -78,8 +79,10 @@ struct X64BackendStackpoint {
|
||||||
enum : uint32_t {
|
enum : uint32_t {
|
||||||
kX64BackendMXCSRModeBit = 0,
|
kX64BackendMXCSRModeBit = 0,
|
||||||
kX64BackendHasReserveBit = 1,
|
kX64BackendHasReserveBit = 1,
|
||||||
kX64BackendNJMOn = 2, //non-java mode bit is currently set. for use in software fp routines
|
kX64BackendNJMOn =
|
||||||
kX64BackendNonIEEEMode = 3, //non-ieee mode is currently enabled for scalar fpu.
|
2, // non-java mode bit is currently set. for use in software fp routines
|
||||||
|
kX64BackendNonIEEEMode =
|
||||||
|
3, // non-ieee mode is currently enabled for scalar fpu.
|
||||||
};
|
};
|
||||||
// located prior to the ctx register
|
// located prior to the ctx register
|
||||||
// some things it would be nice to have be per-emulator instance instead of per
|
// some things it would be nice to have be per-emulator instance instead of per
|
||||||
|
@ -170,8 +173,8 @@ class X64Backend : public Backend {
|
||||||
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
|
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
|
||||||
}
|
}
|
||||||
virtual uint32_t CreateGuestTrampoline(GuestTrampolineProc proc,
|
virtual uint32_t CreateGuestTrampoline(GuestTrampolineProc proc,
|
||||||
void* userdata1,
|
void* userdata1, void* userdata2,
|
||||||
void* userdata2, bool long_term) override;
|
bool long_term) override;
|
||||||
|
|
||||||
virtual void FreeGuestTrampoline(uint32_t trampoline_addr) override;
|
virtual void FreeGuestTrampoline(uint32_t trampoline_addr) override;
|
||||||
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode) override;
|
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode) override;
|
||||||
|
@ -213,6 +216,7 @@ class X64Backend : public Backend {
|
||||||
void* vrsqrtefp_vector_helper = nullptr;
|
void* vrsqrtefp_vector_helper = nullptr;
|
||||||
void* vrsqrtefp_scalar_helper = nullptr;
|
void* vrsqrtefp_scalar_helper = nullptr;
|
||||||
void* frsqrtefp_helper = nullptr;
|
void* frsqrtefp_helper = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if XE_X64_PROFILER_AVAILABLE == 1
|
#if XE_X64_PROFILER_AVAILABLE == 1
|
||||||
GuestProfilerData profiler_data_;
|
GuestProfilerData profiler_data_;
|
||||||
|
|
|
@ -93,7 +93,8 @@ class X64CodeCache : public CodeCache {
|
||||||
// This is picked to be high enough to cover whatever we can reasonably
|
// This is picked to be high enough to cover whatever we can reasonably
|
||||||
// expect. If we hit issues with this it probably means some corner case
|
// expect. If we hit issues with this it probably means some corner case
|
||||||
// in analysis triggering.
|
// in analysis triggering.
|
||||||
//chrispy: raised this, some games that were compiled with low optimization levels can exceed this
|
// chrispy: raised this, some games that were compiled with low optimization
|
||||||
|
// levels can exceed this
|
||||||
static const size_t kMaximumFunctionCount = 1000000;
|
static const size_t kMaximumFunctionCount = 1000000;
|
||||||
|
|
||||||
struct UnwindReservation {
|
struct UnwindReservation {
|
||||||
|
|
|
@ -213,7 +213,8 @@ Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||||
if (unwind_table_count_ >= kMaximumFunctionCount) {
|
if (unwind_table_count_ >= kMaximumFunctionCount) {
|
||||||
// we should not just be ignoring this in release if it happens
|
// we should not just be ignoring this in release if it happens
|
||||||
xe::FatalError(
|
xe::FatalError(
|
||||||
"Unwind table count (unwind_table_count_) exceeded maximum! Please report this to "
|
"Unwind table count (unwind_table_count_) exceeded maximum! Please "
|
||||||
|
"report this to "
|
||||||
"Xenia/Canary developers");
|
"Xenia/Canary developers");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -210,13 +210,15 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||||
// Adding or changing anything here must be matched!
|
// Adding or changing anything here must be matched!
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pick a page to use as the local base as close to the commonly accessed page that contains most backend fields
|
pick a page to use as the local base as close to the commonly accessed page
|
||||||
the sizes that are checked are chosen based on PTE coalescing sizes. zen does 16k or 32k
|
that contains most backend fields the sizes that are checked are chosen
|
||||||
|
based on PTE coalescing sizes. zen does 16k or 32k
|
||||||
*/
|
*/
|
||||||
size_t stack_size = StackLayout::GUEST_STACK_SIZE;
|
size_t stack_size = StackLayout::GUEST_STACK_SIZE;
|
||||||
if (stack_offset < (4096 - sizeof(X64BackendContext))) {
|
if (stack_offset < (4096 - sizeof(X64BackendContext))) {
|
||||||
locals_page_delta_ = 4096;
|
locals_page_delta_ = 4096;
|
||||||
} else if (stack_offset < (16384 - sizeof(X64BackendContext))) {//16k PTE coalescing
|
} else if (stack_offset <
|
||||||
|
(16384 - sizeof(X64BackendContext))) { // 16k PTE coalescing
|
||||||
locals_page_delta_ = 16384;
|
locals_page_delta_ = 16384;
|
||||||
} else if (stack_offset < (32768 - sizeof(X64BackendContext))) {
|
} else if (stack_offset < (32768 - sizeof(X64BackendContext))) {
|
||||||
locals_page_delta_ = 32768;
|
locals_page_delta_ = 32768;
|
||||||
|
@ -224,7 +226,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||||
locals_page_delta_ = 65536;
|
locals_page_delta_ = 65536;
|
||||||
} else {
|
} else {
|
||||||
// extremely unlikely, fall back to stack
|
// extremely unlikely, fall back to stack
|
||||||
stack_size = xe::align<size_t>(StackLayout::GUEST_STACK_SIZE + stack_offset, 16);
|
stack_size =
|
||||||
|
xe::align<size_t>(StackLayout::GUEST_STACK_SIZE + stack_offset, 16);
|
||||||
locals_page_delta_ = 0;
|
locals_page_delta_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1267,12 +1270,13 @@ uintptr_t X64Emitter::PlaceConstData() {
|
||||||
|
|
||||||
std::memcpy(mem, xmm_consts, sizeof(xmm_consts));
|
std::memcpy(mem, xmm_consts, sizeof(xmm_consts));
|
||||||
/*
|
/*
|
||||||
set each 32-bit element of the constant XMMVRsqrteTableBase to be the address of the start of the constant XMMVRsqrteTableStart
|
set each 32-bit element of the constant XMMVRsqrteTableBase to be the
|
||||||
this
|
address of the start of the constant XMMVRsqrteTableStart this
|
||||||
*/
|
*/
|
||||||
vec128_t* deferred_constants = reinterpret_cast<vec128_t*>(mem);
|
vec128_t* deferred_constants = reinterpret_cast<vec128_t*>(mem);
|
||||||
vec128_t* vrsqrte_table_base = &deferred_constants[XMMVRsqrteTableBase];
|
vec128_t* vrsqrte_table_base = &deferred_constants[XMMVRsqrteTableBase];
|
||||||
uint32_t ptr_to_vrsqrte_table32 = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&deferred_constants[XMMVRsqrteTableStart]));
|
uint32_t ptr_to_vrsqrte_table32 = static_cast<uint32_t>(
|
||||||
|
reinterpret_cast<uintptr_t>(&deferred_constants[XMMVRsqrteTableStart]));
|
||||||
*vrsqrte_table_base = vec128i(ptr_to_vrsqrte_table32);
|
*vrsqrte_table_base = vec128i(ptr_to_vrsqrte_table32);
|
||||||
|
|
||||||
memory::Protect(mem, kConstDataSize, memory::PageAccess::kReadOnly, nullptr);
|
memory::Protect(mem, kConstDataSize, memory::PageAccess::kReadOnly, nullptr);
|
||||||
|
@ -1288,8 +1292,10 @@ void X64Emitter::FreeConstData(uintptr_t data) {
|
||||||
Xbyak::Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
Xbyak::Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
||||||
// Load through fixed constant table setup by PlaceConstData.
|
// Load through fixed constant table setup by PlaceConstData.
|
||||||
// It's important that the pointer is not signed, as it will be sign-extended.
|
// It's important that the pointer is not signed, as it will be sign-extended.
|
||||||
void* emitter_data_ptr = backend_->LookupXMMConstantAddress(static_cast<unsigned>(id));
|
void* emitter_data_ptr =
|
||||||
xenia_assert(reinterpret_cast<uintptr_t>(emitter_data_ptr) < (1ULL << 31));//must not have signbit set
|
backend_->LookupXMMConstantAddress(static_cast<unsigned>(id));
|
||||||
|
xenia_assert(reinterpret_cast<uintptr_t>(emitter_data_ptr) <
|
||||||
|
(1ULL << 31)); // must not have signbit set
|
||||||
return ptr[emitter_data_ptr];
|
return ptr[emitter_data_ptr];
|
||||||
}
|
}
|
||||||
// Implies possible StashXmm(0, ...)!
|
// Implies possible StashXmm(0, ...)!
|
||||||
|
|
|
@ -176,7 +176,10 @@ enum XmmConst {
|
||||||
XMMVSRShlByteshuf,
|
XMMVSRShlByteshuf,
|
||||||
XMMVSRMask,
|
XMMVSRMask,
|
||||||
XMMVRsqrteTableStart,
|
XMMVRsqrteTableStart,
|
||||||
XMMVRsqrteTableBase = XMMVRsqrteTableStart + (32 / 4), //32 4-byte elements in table, 4 4-byte elements fit in each xmm
|
XMMVRsqrteTableBase =
|
||||||
|
XMMVRsqrteTableStart +
|
||||||
|
(32 /
|
||||||
|
4), // 32 4-byte elements in table, 4 4-byte elements fit in each xmm
|
||||||
|
|
||||||
};
|
};
|
||||||
using amdfx::xopcompare_e;
|
using amdfx::xopcompare_e;
|
||||||
|
@ -390,6 +393,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
void EmitGetCurrentThreadId();
|
void EmitGetCurrentThreadId();
|
||||||
void EmitTraceUserCallReturn();
|
void EmitTraceUserCallReturn();
|
||||||
static void HandleStackpointOverflowError(ppc::PPCContext* context);
|
static void HandleStackpointOverflowError(ppc::PPCContext* context);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Processor* processor_ = nullptr;
|
Processor* processor_ = nullptr;
|
||||||
X64Backend* backend_ = nullptr;
|
X64Backend* backend_ = nullptr;
|
||||||
|
|
|
@ -398,8 +398,7 @@ struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
XE_MAYBE_UNUSED
|
XE_MAYBE_UNUSED static const T GetTempReg(X64Emitter& e);
|
||||||
static const T GetTempReg(X64Emitter& e);
|
|
||||||
template <>
|
template <>
|
||||||
XE_MAYBE_UNUSED const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
XE_MAYBE_UNUSED const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
||||||
return e.al;
|
return e.al;
|
||||||
|
|
|
@ -705,7 +705,8 @@ struct STORE_LOCAL_I16
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
// e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2);
|
// e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2);
|
||||||
if (LocalStoreMayUseMembaseLow(e, i)) {
|
if (LocalStoreMayUseMembaseLow(e, i)) {
|
||||||
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()], e.GetMembaseReg().cvt16());
|
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()],
|
||||||
|
e.GetMembaseReg().cvt16());
|
||||||
} else {
|
} else {
|
||||||
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
||||||
}
|
}
|
||||||
|
@ -716,7 +717,8 @@ struct STORE_LOCAL_I32
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
// e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2);
|
// e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2);
|
||||||
if (LocalStoreMayUseMembaseLow(e, i)) {
|
if (LocalStoreMayUseMembaseLow(e, i)) {
|
||||||
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()], e.GetMembaseReg().cvt32());
|
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()],
|
||||||
|
e.GetMembaseReg().cvt32());
|
||||||
} else {
|
} else {
|
||||||
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2120,9 +2120,9 @@ struct RSQRT_V128 : Sequence<RSQRT_V128, I<OPCODE_RSQRT, V128Op, V128Op>> {
|
||||||
e.ChangeMxcsrMode(MXCSRMode::Vmx);
|
e.ChangeMxcsrMode(MXCSRMode::Vmx);
|
||||||
Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm3);
|
Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm3);
|
||||||
/*
|
/*
|
||||||
the vast majority of inputs to vrsqrte come from vmsum3 or vmsum4 as part
|
the vast majority of inputs to vrsqrte come from vmsum3 or vmsum4 as
|
||||||
of a vector normalization sequence. in fact, its difficult to find uses of vrsqrte in titles
|
part of a vector normalization sequence. in fact, its difficult to find
|
||||||
that have inputs which do not come from vmsum.
|
uses of vrsqrte in titles that have inputs which do not come from vmsum.
|
||||||
*/
|
*/
|
||||||
if (i.src1.value && i.src1.value->AllFloatVectorLanesSameValue()) {
|
if (i.src1.value && i.src1.value->AllFloatVectorLanesSameValue()) {
|
||||||
e.vmovss(e.xmm0, src1);
|
e.vmovss(e.xmm0, src1);
|
||||||
|
@ -3193,8 +3193,7 @@ struct SET_ROUNDING_MODE_I32
|
||||||
|
|
||||||
if (constant_value & 4) {
|
if (constant_value & 4) {
|
||||||
e.or_(flags_ptr, 1U << kX64BackendNonIEEEMode);
|
e.or_(flags_ptr, 1U << kX64BackendNonIEEEMode);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
e.btr(flags_ptr, kX64BackendNonIEEEMode);
|
e.btr(flags_ptr, kX64BackendNonIEEEMode);
|
||||||
}
|
}
|
||||||
e.mov(e.dword[e.rsp + StackLayout::GUEST_SCRATCH], e.eax);
|
e.mov(e.dword[e.rsp + StackLayout::GUEST_SCRATCH], e.eax);
|
||||||
|
|
|
@ -122,10 +122,12 @@ class StackLayout {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static const size_t GUEST_STACK_SIZE = 104;
|
static const size_t GUEST_STACK_SIZE = 104;
|
||||||
//was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off. instead, can be used as a temporary in sequences
|
// was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off.
|
||||||
|
// instead, can be used as a temporary in sequences
|
||||||
static const size_t GUEST_SCRATCH = 0;
|
static const size_t GUEST_SCRATCH = 0;
|
||||||
|
|
||||||
//when profiling is on, this stores the nanosecond time at the start of the function
|
// when profiling is on, this stores the nanosecond time at the start of the
|
||||||
|
// function
|
||||||
static const size_t GUEST_PROFILER_START = 80;
|
static const size_t GUEST_PROFILER_START = 80;
|
||||||
static const size_t GUEST_RET_ADDR = 88;
|
static const size_t GUEST_RET_ADDR = 88;
|
||||||
static const size_t GUEST_CALL_RET_ADDR = 96;
|
static const size_t GUEST_CALL_RET_ADDR = 96;
|
||||||
|
|
|
@ -29,8 +29,7 @@ namespace x64 {
|
||||||
|
|
||||||
bool trace_enabled = true;
|
bool trace_enabled = true;
|
||||||
|
|
||||||
#define THREAD_MATCH \
|
#define THREAD_MATCH (!TARGET_THREAD || ppc_context->thread_id == TARGET_THREAD)
|
||||||
(!TARGET_THREAD || ppc_context->thread_id == TARGET_THREAD)
|
|
||||||
#define IFLUSH()
|
#define IFLUSH()
|
||||||
#define IPRINT(s) \
|
#define IPRINT(s) \
|
||||||
if (trace_enabled && THREAD_MATCH) \
|
if (trace_enabled && THREAD_MATCH) \
|
||||||
|
|
|
@ -1372,9 +1372,10 @@ bool SimplificationPass::SimplifyVectorOps(hir::Instr* i,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
splatting a 32-bit value extracted from a vector where all 4 32-bit values are the same should be eliminated and
|
splatting a 32-bit value extracted from a vector where all 4 32-bit values
|
||||||
instead use the vector extracted from, which will be identical
|
are the same should be eliminated and instead use the vector extracted from,
|
||||||
have seen this happen, some games vmsum and then splat the low float to all 4 floats, even though it already is there
|
which will be identical have seen this happen, some games vmsum and then
|
||||||
|
splat the low float to all 4 floats, even though it already is there
|
||||||
*/
|
*/
|
||||||
if (opc == OPCODE_SPLAT) {
|
if (opc == OPCODE_SPLAT) {
|
||||||
if (i->dest->type == VEC128_TYPE) {
|
if (i->dest->type == VEC128_TYPE) {
|
||||||
|
@ -1388,7 +1389,6 @@ bool SimplificationPass::SimplifyVectorOps(hir::Instr* i,
|
||||||
if (defining_opcode == OPCODE_EXTRACT) {
|
if (defining_opcode == OPCODE_EXTRACT) {
|
||||||
auto value_extracted_from = splat_input_definition->src1.value;
|
auto value_extracted_from = splat_input_definition->src1.value;
|
||||||
if (value_extracted_from->type == VEC128_TYPE) {
|
if (value_extracted_from->type == VEC128_TYPE) {
|
||||||
|
|
||||||
xenia_assert(splat_input_definition->dest->type == splat_type);
|
xenia_assert(splat_input_definition->dest->type == splat_type);
|
||||||
|
|
||||||
if (value_extracted_from->AllFloatVectorLanesSameValue()) {
|
if (value_extracted_from->AllFloatVectorLanesSameValue()) {
|
||||||
|
|
|
@ -95,7 +95,6 @@ class Export {
|
||||||
uint32_t variable_ptr;
|
uint32_t variable_ptr;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
||||||
// Trampoline that is called from the guest-to-host thunk.
|
// Trampoline that is called from the guest-to-host thunk.
|
||||||
// Expects only PPC context as first arg.
|
// Expects only PPC context as first arg.
|
||||||
ExportTrampoline trampoline;
|
ExportTrampoline trampoline;
|
||||||
|
|
|
@ -115,7 +115,6 @@ uintptr_t GuestFunction::MapGuestAddressToMachineCode(
|
||||||
return reinterpret_cast<uintptr_t>(machine_code()) + entry->code_offset;
|
return reinterpret_cast<uintptr_t>(machine_code()) + entry->code_offset;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,11 @@ class Instr {
|
||||||
void MoveBefore(Instr* other);
|
void MoveBefore(Instr* other);
|
||||||
void Replace(const OpcodeInfo* new_opcode, uint16_t new_flags);
|
void Replace(const OpcodeInfo* new_opcode, uint16_t new_flags);
|
||||||
void UnlinkAndNOP();
|
void UnlinkAndNOP();
|
||||||
//chrispy: wanted to change this one to Remove, but i changed Remove's name to UnlinkAndNOP,
|
// chrispy: wanted to change this one to Remove, but i changed Remove's name
|
||||||
//so if changes happened in master that we wanted to port over, and those changes used Remove, then we would have a lot of issues that the cause of would
|
// to UnlinkAndNOP, so if changes happened in master that we wanted to port
|
||||||
//be difficult to track
|
// over, and those changes used Remove, then we would have a lot of issues
|
||||||
//^todo: rework this comment, im frazzled
|
// that the cause of would be difficult to track ^todo: rework this comment,
|
||||||
|
// im frazzled
|
||||||
void Deallocate();
|
void Deallocate();
|
||||||
const OpcodeInfo* GetOpcodeInfo() const { return opcode; }
|
const OpcodeInfo* GetOpcodeInfo() const { return opcode; }
|
||||||
// if opcode is null, we have bigger problems
|
// if opcode is null, we have bigger problems
|
||||||
|
|
|
@ -30,9 +30,7 @@ class Label {
|
||||||
// this will later be used as an input to xbyak. xbyak only accepts
|
// this will later be used as an input to xbyak. xbyak only accepts
|
||||||
// std::string as a value, not passed by reference, so precomputing the
|
// std::string as a value, not passed by reference, so precomputing the
|
||||||
// stringification does not help
|
// stringification does not help
|
||||||
std::string GetIdString() {
|
std::string GetIdString() { return std::to_string(id); }
|
||||||
return std::to_string(id);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace hir
|
} // namespace hir
|
||||||
|
|
|
@ -1819,7 +1819,8 @@ re_enter:
|
||||||
xenia_assert(for_value->IsConstant());
|
xenia_assert(for_value->IsConstant());
|
||||||
|
|
||||||
auto&& constant_value = for_value->constant.v128;
|
auto&& constant_value = for_value->constant.v128;
|
||||||
for (unsigned constant_lane_index = 1; constant_lane_index < 4; ++constant_lane_index) {
|
for (unsigned constant_lane_index = 1; constant_lane_index < 4;
|
||||||
|
++constant_lane_index) {
|
||||||
if (constant_value.u32[0] != constant_value.u32[constant_lane_index]) {
|
if (constant_value.u32[0] != constant_value.u32[constant_lane_index]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1845,8 +1846,9 @@ re_enter:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// if splat of 32-bit value type, return true
|
// if splat of 32-bit value type, return true
|
||||||
//technically a splat of int16 or int8 would also produce the same "float" in all lanes
|
// technically a splat of int16 or int8 would also produce the same "float" in
|
||||||
//but i think its best to keep this function focused on specifically float data
|
// all lanes but i think its best to keep this function focused on
|
||||||
|
// specifically float data
|
||||||
if (definition_opcode_number == OPCODE_SPLAT) {
|
if (definition_opcode_number == OPCODE_SPLAT) {
|
||||||
if (definition->dest->type == VEC128_TYPE) {
|
if (definition->dest->type == VEC128_TYPE) {
|
||||||
auto splat_src_value_type = definition->src1.value->type;
|
auto splat_src_value_type = definition->src1.value->type;
|
||||||
|
@ -1883,7 +1885,6 @@ re_enter:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace hir
|
} // namespace hir
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -621,6 +621,7 @@ class Value {
|
||||||
bool AllFloatVectorLanesSameValue() const {
|
bool AllFloatVectorLanesSameValue() const {
|
||||||
return Value::AllFloatVectorLanesSameValue(this);
|
return Value::AllFloatVectorLanesSameValue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
returns true if for_value (which must be VEC128_TYPE) has the same value in
|
returns true if for_value (which must be VEC128_TYPE) has the same value in
|
||||||
|
|
|
@ -48,7 +48,9 @@ class MMIOHandler {
|
||||||
typedef uint32_t (*HostToGuestVirtual)(const void* context,
|
typedef uint32_t (*HostToGuestVirtual)(const void* context,
|
||||||
const void* host_address);
|
const void* host_address);
|
||||||
typedef bool (*AccessViolationCallback)(
|
typedef bool (*AccessViolationCallback)(
|
||||||
global_unique_lock_type global_lock_locked_once, //not passed by reference with const like the others?
|
global_unique_lock_type
|
||||||
|
global_lock_locked_once, // not passed by reference with const like
|
||||||
|
// the others?
|
||||||
void* context, void* host_address, bool is_write);
|
void* context, void* host_address, bool is_write);
|
||||||
|
|
||||||
// access_violation_callback is called with global_critical_region locked once
|
// access_violation_callback is called with global_critical_region locked once
|
||||||
|
|
|
@ -55,6 +55,7 @@ class Module {
|
||||||
bool ReadMap(const char* file_name);
|
bool ReadMap(const char* file_name);
|
||||||
|
|
||||||
virtual void Precompile() {}
|
virtual void Precompile() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual std::unique_ptr<Function> CreateFunction(uint32_t address) = 0;
|
virtual std::unique_ptr<Function> CreateFunction(uint32_t address) = 0;
|
||||||
|
|
||||||
|
|
|
@ -789,7 +789,6 @@ int InstrEmit_mtspr(PPCHIRBuilder& f, const InstrData& i) {
|
||||||
// code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and
|
// code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and
|
||||||
// without the lock here threads can livelock.
|
// without the lock here threads can livelock.
|
||||||
|
|
||||||
|
|
||||||
// 0x400 = debug singlestep i think
|
// 0x400 = debug singlestep i think
|
||||||
// ive seen 0x8000 used in kernel code
|
// ive seen 0x8000 used in kernel code
|
||||||
int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) {
|
int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) {
|
||||||
|
|
|
@ -106,11 +106,12 @@ bool PPCFrontend::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PPCFrontend::DeclareFunction(GuestFunction* function) {
|
bool PPCFrontend::DeclareFunction(GuestFunction* function) {
|
||||||
|
// chrispy: make sure we aren't declaring a function that is actually padding
|
||||||
//chrispy: make sure we aren't declaring a function that is actually padding data, this will mess up PPCScanner and is hard to debug
|
// data, this will mess up PPCScanner and is hard to debug wow, this halo
|
||||||
//wow, this halo reach actually has branches into 0 opcodes, look into further
|
// reach actually has branches into 0 opcodes, look into further
|
||||||
// xenia_assert(*reinterpret_cast<const uint32_t*>(
|
// xenia_assert(*reinterpret_cast<const uint32_t*>(
|
||||||
// this->memory()->TranslateVirtual(function->address())) != 0);
|
// this->memory()->TranslateVirtual(function->address())) !=
|
||||||
|
// 0);
|
||||||
// Could scan or something here.
|
// Could scan or something here.
|
||||||
// Could also check to see if it's a well-known function type and classify
|
// Could also check to see if it's a well-known function type and classify
|
||||||
// for later.
|
// for later.
|
||||||
|
|
|
@ -80,8 +80,10 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
|
|
||||||
void StoreReserved(Value* val);
|
void StoreReserved(Value* val);
|
||||||
Value* LoadReserved();
|
Value* LoadReserved();
|
||||||
//calls original impl in hirbuilder, but also records the is_return_site bit into flags in the guestmodule
|
// calls original impl in hirbuilder, but also records the is_return_site bit
|
||||||
|
// into flags in the guestmodule
|
||||||
void SetReturnAddress(Value* value);
|
void SetReturnAddress(Value* value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MaybeBreakOnInstruction(uint32_t address);
|
void MaybeBreakOnInstruction(uint32_t address);
|
||||||
void AnnotateLabel(uint32_t address, Label* label);
|
void AnnotateLabel(uint32_t address, Label* label);
|
||||||
|
|
|
@ -1326,8 +1326,8 @@ uint32_t Processor::GuestAtomicDecrement32(ppc::PPCContext* context,
|
||||||
|
|
||||||
uint32_t Processor::GuestAtomicOr32(ppc::PPCContext* context,
|
uint32_t Processor::GuestAtomicOr32(ppc::PPCContext* context,
|
||||||
uint32_t guest_address, uint32_t mask) {
|
uint32_t guest_address, uint32_t mask) {
|
||||||
return xe::byte_swap(xe::atomic_or(
|
return xe::byte_swap(
|
||||||
context->TranslateVirtual<volatile int32_t*>(guest_address),
|
xe::atomic_or(context->TranslateVirtual<volatile int32_t*>(guest_address),
|
||||||
xe::byte_swap(mask)));
|
xe::byte_swap(mask)));
|
||||||
}
|
}
|
||||||
uint32_t Processor::GuestAtomicXor32(ppc::PPCContext* context,
|
uint32_t Processor::GuestAtomicXor32(ppc::PPCContext* context,
|
||||||
|
|
|
@ -77,8 +77,7 @@ ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
||||||
|
|
||||||
// Allocate with 64b alignment.
|
// Allocate with 64b alignment.
|
||||||
|
|
||||||
context_ = reinterpret_cast<ppc::PPCContext*>(
|
context_ = reinterpret_cast<ppc::PPCContext*>(AllocateContext());
|
||||||
AllocateContext());
|
|
||||||
processor->backend()->InitializeBackendContext(context_);
|
processor->backend()->InitializeBackendContext(context_);
|
||||||
assert_true(((uint64_t)context_ & 0x3F) == 0);
|
assert_true(((uint64_t)context_ & 0x3F) == 0);
|
||||||
std::memset(context_, 0, sizeof(ppc::PPCContext));
|
std::memset(context_, 0, sizeof(ppc::PPCContext));
|
||||||
|
@ -98,8 +97,10 @@ ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
||||||
context_->msr = 0x9030; // dumped from a real 360, 0x8000
|
context_->msr = 0x9030; // dumped from a real 360, 0x8000
|
||||||
|
|
||||||
// this register can be used for arbitrary data according to the PPC docs
|
// this register can be used for arbitrary data according to the PPC docs
|
||||||
//but the suggested use is to mark which vector registers are in use, for faster save/restore
|
// but the suggested use is to mark which vector registers are in use, for
|
||||||
//it seems unlikely anything uses this, especially since we have way more than 32 vrs, but setting it to all ones seems closer to correct than 0
|
// faster save/restore it seems unlikely anything uses this, especially since
|
||||||
|
// we have way more than 32 vrs, but setting it to all ones seems closer to
|
||||||
|
// correct than 0
|
||||||
context_->vrsave = ~0u;
|
context_->vrsave = ~0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,9 +230,7 @@ class Emulator {
|
||||||
xe::Delegate<> on_exit;
|
xe::Delegate<> on_exit;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum : uint64_t {
|
enum : uint64_t { EmulatorFlagDisclaimerAcknowledged = 1ULL << 0 };
|
||||||
EmulatorFlagDisclaimerAcknowledged = 1ULL << 0
|
|
||||||
};
|
|
||||||
static uint64_t GetPersistentEmulatorFlags();
|
static uint64_t GetPersistentEmulatorFlags();
|
||||||
static void SetPersistentEmulatorFlags(uint64_t new_flags);
|
static void SetPersistentEmulatorFlags(uint64_t new_flags);
|
||||||
static std::string CanonicalizeFileExtension(
|
static std::string CanonicalizeFileExtension(
|
||||||
|
|
|
@ -100,11 +100,14 @@ bool CommandProcessor::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
worker_running_ = true;
|
worker_running_ = true;
|
||||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
worker_thread_ =
|
||||||
new kernel::XHostThread(kernel_state_, 128 * 1024, 0, [this]() {
|
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||||
|
kernel_state_, 128 * 1024, 0,
|
||||||
|
[this]() {
|
||||||
WorkerThreadMain();
|
WorkerThreadMain();
|
||||||
return 0;
|
return 0;
|
||||||
}, kernel_state_->GetIdleProcess()));
|
},
|
||||||
|
kernel_state_->GetIdleProcess()));
|
||||||
worker_thread_->set_name("GPU Commands");
|
worker_thread_->set_name("GPU Commands");
|
||||||
worker_thread_->Create();
|
worker_thread_->Create();
|
||||||
|
|
||||||
|
@ -270,7 +273,8 @@ void CommandProcessor::WorkerThreadMain() {
|
||||||
|
|
||||||
// TODO(benvanik): use reader->Read_update_freq_ and only issue after moving
|
// TODO(benvanik): use reader->Read_update_freq_ and only issue after moving
|
||||||
// that many indices.
|
// that many indices.
|
||||||
// Keep in mind that the gpu also updates the cpu-side copy if the write pointer and read pointer would be equal
|
// Keep in mind that the gpu also updates the cpu-side copy if the write
|
||||||
|
// pointer and read pointer would be equal
|
||||||
if (read_ptr_writeback_ptr_) {
|
if (read_ptr_writeback_ptr_) {
|
||||||
xe::store_and_swap<uint32_t>(
|
xe::store_and_swap<uint32_t>(
|
||||||
memory_->TranslatePhysical(read_ptr_writeback_ptr_), read_ptr_index_);
|
memory_->TranslatePhysical(read_ptr_writeback_ptr_), read_ptr_index_);
|
||||||
|
@ -360,9 +364,8 @@ void CommandProcessor::EnableReadPointerWriteBack(uint32_t ptr,
|
||||||
XE_NOINLINE XE_COLD void CommandProcessor::LogKickoffInitator(uint32_t value) {
|
XE_NOINLINE XE_COLD void CommandProcessor::LogKickoffInitator(uint32_t value) {
|
||||||
cpu::backend::GuestPseudoStackTrace st;
|
cpu::backend::GuestPseudoStackTrace st;
|
||||||
|
|
||||||
if (logging::internal::ShouldLog(LogLevel::Debug) && kernel_state_->processor()
|
if (logging::internal::ShouldLog(LogLevel::Debug) &&
|
||||||
->backend()
|
kernel_state_->processor()->backend()->PopulatePseudoStacktrace(&st)) {
|
||||||
->PopulatePseudoStacktrace(&st)) {
|
|
||||||
logging::LoggerBatch<LogLevel::Debug> log_initiator{};
|
logging::LoggerBatch<LogLevel::Debug> log_initiator{};
|
||||||
|
|
||||||
log_initiator("Updating read ptr to {}, initiator stacktrace below\n",
|
log_initiator("Updating read ptr to {}, initiator stacktrace below\n",
|
||||||
|
@ -390,7 +393,8 @@ void CommandProcessor::UpdateWritePointer(uint32_t value) {
|
||||||
|
|
||||||
void CommandProcessor::LogRegisterSet(uint32_t register_index, uint32_t value) {
|
void CommandProcessor::LogRegisterSet(uint32_t register_index, uint32_t value) {
|
||||||
#if XE_ENABLE_GPU_REG_WRITE_LOGGING == 1
|
#if XE_ENABLE_GPU_REG_WRITE_LOGGING == 1
|
||||||
if (cvars::log_guest_driven_gpu_register_written_values && logging::internal::ShouldLog(LogLevel::Debug)) {
|
if (cvars::log_guest_driven_gpu_register_written_values &&
|
||||||
|
logging::internal::ShouldLog(LogLevel::Debug)) {
|
||||||
const RegisterInfo* reginfo = RegisterFile::GetRegisterInfo(register_index);
|
const RegisterInfo* reginfo = RegisterFile::GetRegisterInfo(register_index);
|
||||||
|
|
||||||
if (!reginfo) {
|
if (!reginfo) {
|
||||||
|
@ -734,7 +738,6 @@ void CommandProcessor::PrepareForWait() { trace_writer_.Flush(); }
|
||||||
|
|
||||||
void CommandProcessor::ReturnFromWait() {}
|
void CommandProcessor::ReturnFromWait() {}
|
||||||
|
|
||||||
|
|
||||||
void CommandProcessor::InitializeTrace() {
|
void CommandProcessor::InitializeTrace() {
|
||||||
// Write the initial register values, to be loaded directly into the
|
// Write the initial register values, to be loaded directly into the
|
||||||
// RegisterFile since all registers, including those that may have side
|
// RegisterFile since all registers, including those that may have side
|
||||||
|
|
|
@ -225,7 +225,6 @@ class CommandProcessor {
|
||||||
virtual void PrepareForWait();
|
virtual void PrepareForWait();
|
||||||
virtual void ReturnFromWait();
|
virtual void ReturnFromWait();
|
||||||
|
|
||||||
|
|
||||||
virtual void OnPrimaryBufferEnd() {}
|
virtual void OnPrimaryBufferEnd() {}
|
||||||
|
|
||||||
#include "pm4_command_processor_declare.h"
|
#include "pm4_command_processor_declare.h"
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||||
#include "xenia/gpu/draw_util.h"
|
#include "xenia/gpu/draw_util.h"
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
#include "xenia/gpu/gpu_flags.h"
|
||||||
|
#include "xenia/gpu/packet_disassembler.h"
|
||||||
#include "xenia/gpu/registers.h"
|
#include "xenia/gpu/registers.h"
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
#include "xenia/gpu/packet_disassembler.h"
|
|
||||||
#include "xenia/ui/d3d12/d3d12_presenter.h"
|
#include "xenia/ui/d3d12/d3d12_presenter.h"
|
||||||
#include "xenia/ui/d3d12/d3d12_util.h"
|
#include "xenia/ui/d3d12/d3d12_util.h"
|
||||||
|
|
||||||
|
@ -65,7 +65,6 @@ void D3D12SaveGPUSetting(D3D12GPUSetting setting, uint64_t value) {
|
||||||
|
|
||||||
namespace d3d12 {
|
namespace d3d12 {
|
||||||
|
|
||||||
|
|
||||||
// Generated with `xb buildshaders`.
|
// Generated with `xb buildshaders`.
|
||||||
namespace shaders {
|
namespace shaders {
|
||||||
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h"
|
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h"
|
||||||
|
@ -4992,7 +4991,8 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
||||||
}
|
}
|
||||||
// Null SRV + UAV + EDRAM.
|
// Null SRV + UAV + EDRAM.
|
||||||
gpu_handle_shared_memory_uav_and_edram_ = view_gpu_handle;
|
gpu_handle_shared_memory_uav_and_edram_ = view_gpu_handle;
|
||||||
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle, nullptr, 0);
|
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle,
|
||||||
|
nullptr, 0);
|
||||||
view_cpu_handle.ptr += descriptor_size_view;
|
view_cpu_handle.ptr += descriptor_size_view;
|
||||||
view_gpu_handle.ptr += descriptor_size_view;
|
view_gpu_handle.ptr += descriptor_size_view;
|
||||||
shared_memory_->WriteRawUAVDescriptor(view_cpu_handle);
|
shared_memory_->WriteRawUAVDescriptor(view_cpu_handle);
|
||||||
|
|
|
@ -245,7 +245,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||||
void WriteFetchFromMem(uint32_t start_index, uint32_t* base,
|
void WriteFetchFromMem(uint32_t start_index, uint32_t* base,
|
||||||
uint32_t num_registers);
|
uint32_t num_registers);
|
||||||
|
|
||||||
void WritePossiblySpecialRegistersFromMem(uint32_t start_index, uint32_t* base,
|
void WritePossiblySpecialRegistersFromMem(uint32_t start_index,
|
||||||
|
uint32_t* base,
|
||||||
uint32_t num_registers);
|
uint32_t num_registers);
|
||||||
template <uint32_t register_lower_bound, uint32_t register_upper_bound>
|
template <uint32_t register_lower_bound, uint32_t register_upper_bound>
|
||||||
XE_FORCEINLINE void WriteRegisterRangeFromMem_WithKnownBound(
|
XE_FORCEINLINE void WriteRegisterRangeFromMem_WithKnownBound(
|
||||||
|
@ -262,8 +263,7 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||||
uint32_t base,
|
uint32_t base,
|
||||||
uint32_t num_registers);
|
uint32_t num_registers);
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
void WriteOneRegisterFromRing(uint32_t base,
|
void WriteOneRegisterFromRing(uint32_t base, uint32_t num_times);
|
||||||
uint32_t num_times);
|
|
||||||
|
|
||||||
XE_FORCEINLINE
|
XE_FORCEINLINE
|
||||||
void WriteALURangeFromRing(xe::RingBuffer* ring, uint32_t base,
|
void WriteALURangeFromRing(xe::RingBuffer* ring, uint32_t base,
|
||||||
|
@ -795,7 +795,6 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||||
draw_util::GetViewportInfoArgs previous_viewport_info_args_;
|
draw_util::GetViewportInfoArgs previous_viewport_info_args_;
|
||||||
draw_util::ViewportInfo previous_viewport_info_;
|
draw_util::ViewportInfo previous_viewport_info_;
|
||||||
|
|
||||||
|
|
||||||
std::atomic<bool> pix_capture_requested_ = false;
|
std::atomic<bool> pix_capture_requested_ = false;
|
||||||
bool pix_capturing_;
|
bool pix_capturing_;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/literals.h"
|
#include "xenia/base/literals.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
|
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
namespace d3d12 {
|
namespace d3d12 {
|
||||||
|
@ -32,7 +32,8 @@ class DeferredCommandList {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t MAX_SIZEOF_COMMANDLIST = 65536 * 128; // around 8 mb
|
static constexpr size_t MAX_SIZEOF_COMMANDLIST = 65536 * 128; // around 8 mb
|
||||||
/*
|
/*
|
||||||
chrispy: upped from 1_MiB to 4_MiB, m:durandal hits frequent resizes in large open maps
|
chrispy: upped from 1_MiB to 4_MiB, m:durandal hits frequent resizes in
|
||||||
|
large open maps
|
||||||
*/
|
*/
|
||||||
DeferredCommandList(const D3D12CommandProcessor& command_processor,
|
DeferredCommandList(const D3D12CommandProcessor& command_processor,
|
||||||
size_t initial_size_bytes = MAX_SIZEOF_COMMANDLIST);
|
size_t initial_size_bytes = MAX_SIZEOF_COMMANDLIST);
|
||||||
|
|
|
@ -552,8 +552,7 @@ void GetHostViewportInfo(GetViewportInfoArgs* XE_RESTRICT args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <bool clamp_to_surface_pitch>
|
template <bool clamp_to_surface_pitch>
|
||||||
static inline
|
static inline void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
||||||
void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
|
||||||
Scissor& XE_RESTRICT scissor_out) {
|
Scissor& XE_RESTRICT scissor_out) {
|
||||||
#if XE_ARCH_AMD64 == 1
|
#if XE_ARCH_AMD64 == 1
|
||||||
auto pa_sc_window_scissor_tl = regs.Get<reg::PA_SC_WINDOW_SCISSOR_TL>();
|
auto pa_sc_window_scissor_tl = regs.Get<reg::PA_SC_WINDOW_SCISSOR_TL>();
|
||||||
|
@ -623,8 +622,7 @@ void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
||||||
// interlock-based custom RB implementations) and using conventional render
|
// interlock-based custom RB implementations) and using conventional render
|
||||||
// targets, but padded to EDRAM tiles.
|
// targets, but padded to EDRAM tiles.
|
||||||
tmp1 = _mm_blend_epi16(
|
tmp1 = _mm_blend_epi16(
|
||||||
tmp1, _mm_min_epi32(tmp1, _mm_set1_epi32(surface_pitch)),
|
tmp1, _mm_min_epi32(tmp1, _mm_set1_epi32(surface_pitch)), 0b00110011);
|
||||||
0b00110011);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp1 = _mm_max_epi32(tmp1, _mm_setzero_si128());
|
tmp1 = _mm_max_epi32(tmp1, _mm_setzero_si128());
|
||||||
|
|
|
@ -25,10 +25,10 @@
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/gpu/command_processor.h"
|
#include "xenia/gpu/command_processor.h"
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
#include "xenia/gpu/gpu_flags.h"
|
||||||
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/ui/graphics_provider.h"
|
#include "xenia/ui/graphics_provider.h"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/ui/windowed_app_context.h"
|
#include "xenia/ui/windowed_app_context.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
|
||||||
DEFINE_bool(
|
DEFINE_bool(
|
||||||
store_shaders, true,
|
store_shaders, true,
|
||||||
"Store shaders persistently and load them when loading games to avoid "
|
"Store shaders persistently and load them when loading games to avoid "
|
||||||
|
@ -102,8 +102,10 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
|
|
||||||
// 60hz vsync timer.
|
// 60hz vsync timer.
|
||||||
vsync_worker_running_ = true;
|
vsync_worker_running_ = true;
|
||||||
vsync_worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
vsync_worker_thread_ =
|
||||||
new kernel::XHostThread(kernel_state_, 128 * 1024, 0, [this]() {
|
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||||
|
kernel_state_, 128 * 1024, 0,
|
||||||
|
[this]() {
|
||||||
const double vsync_duration_d =
|
const double vsync_duration_d =
|
||||||
cvars::vsync
|
cvars::vsync
|
||||||
? std::max<double>(
|
? std::max<double>(
|
||||||
|
@ -117,13 +119,15 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
const uint64_t current_time = Clock::QueryGuestTickCount();
|
const uint64_t current_time = Clock::QueryGuestTickCount();
|
||||||
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
||||||
const uint64_t time_delta = current_time - last_frame_time;
|
const uint64_t time_delta = current_time - last_frame_time;
|
||||||
const double elapsed_d = static_cast<double>(time_delta) /
|
const double elapsed_d =
|
||||||
|
static_cast<double>(time_delta) /
|
||||||
(static_cast<double>(tick_freq) / 1000.0);
|
(static_cast<double>(tick_freq) / 1000.0);
|
||||||
if (elapsed_d >= vsync_duration_d) {
|
if (elapsed_d >= vsync_duration_d) {
|
||||||
last_frame_time = current_time;
|
last_frame_time = current_time;
|
||||||
|
|
||||||
// TODO(disjtqz): should recalculate the remaining time to a vblank
|
// TODO(disjtqz): should recalculate the remaining time to a
|
||||||
// after MarkVblank, no idea how long the guest code normally takes
|
// vblank after MarkVblank, no idea how long the guest code
|
||||||
|
// normally takes
|
||||||
MarkVblank();
|
MarkVblank();
|
||||||
if (cvars::vsync) {
|
if (cvars::vsync) {
|
||||||
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
|
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
|
||||||
|
@ -138,7 +142,8 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}, kernel_state->GetIdleProcess()));
|
},
|
||||||
|
kernel_state->GetIdleProcess()));
|
||||||
// As we run vblank interrupts the debugger must be able to suspend us.
|
// As we run vblank interrupts the debugger must be able to suspend us.
|
||||||
vsync_worker_thread_->set_can_debugger_suspend(true);
|
vsync_worker_thread_->set_can_debugger_suspend(true);
|
||||||
vsync_worker_thread_->set_name("GPU VSync");
|
vsync_worker_thread_->set_name("GPU VSync");
|
||||||
|
@ -267,7 +272,8 @@ void GraphicsSystem::SetInterruptCallback(uint32_t callback,
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
|
void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
|
||||||
kernel_state()->EmulateCPInterruptDPC(interrupt_callback_,interrupt_callback_data_, source, cpu);
|
kernel_state()->EmulateCPInterruptDPC(interrupt_callback_,
|
||||||
|
interrupt_callback_data_, source, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsSystem::MarkVblank() {
|
void GraphicsSystem::MarkVblank() {
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
#else
|
#else
|
||||||
#define PM4_OVERRIDE
|
#define PM4_OVERRIDE
|
||||||
#endif
|
#endif
|
||||||
void ExecuteIndirectBuffer(uint32_t ptr,
|
void ExecuteIndirectBuffer(uint32_t ptr, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
virtual uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index)
|
virtual uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index)
|
||||||
XE_RESTRICT PM4_OVERRIDE;
|
XE_RESTRICT PM4_OVERRIDE;
|
||||||
virtual bool ExecutePacket() PM4_OVERRIDE;
|
virtual bool ExecutePacket() PM4_OVERRIDE;
|
||||||
|
@ -26,16 +25,12 @@ bool ExecutePacketType2( uint32_t packet) XE_RESTRICT;
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3(uint32_t packet) XE_RESTRICT;
|
bool ExecutePacketType3(uint32_t packet) XE_RESTRICT;
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_ME_INIT( uint32_t packet,
|
bool ExecutePacketType3_ME_INIT(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
bool ExecutePacketType3_NOP(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
bool ExecutePacketType3_NOP( uint32_t packet,
|
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_INTERRUPT( uint32_t packet,
|
bool ExecutePacketType3_INTERRUPT(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_XE_SWAP( uint32_t packet,
|
bool ExecutePacketType3_XE_SWAP(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
bool ExecutePacketType3_INDIRECT_BUFFER(uint32_t packet,
|
bool ExecutePacketType3_INDIRECT_BUFFER(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
@ -43,17 +38,13 @@ XE_NOINLINE
|
||||||
bool ExecutePacketType3_WAIT_REG_MEM(uint32_t packet,
|
bool ExecutePacketType3_WAIT_REG_MEM(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_REG_RMW( uint32_t packet,
|
bool ExecutePacketType3_REG_RMW(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
bool ExecutePacketType3_REG_TO_MEM( uint32_t packet,
|
bool ExecutePacketType3_REG_TO_MEM(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_MEM_WRITE( uint32_t packet,
|
bool ExecutePacketType3_MEM_WRITE(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
bool ExecutePacketType3_COND_WRITE( uint32_t packet,
|
bool ExecutePacketType3_COND_WRITE(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
bool ExecutePacketType3_EVENT_WRITE(uint32_t packet,
|
bool ExecutePacketType3_EVENT_WRITE(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
@ -67,13 +58,11 @@ XE_NOINLINE
|
||||||
bool ExecutePacketType3_EVENT_WRITE_ZPD(uint32_t packet,
|
bool ExecutePacketType3_EVENT_WRITE_ZPD(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
|
||||||
bool ExecutePacketType3Draw( uint32_t packet,
|
bool ExecutePacketType3Draw(uint32_t packet, const char* opcode_name,
|
||||||
const char* opcode_name,
|
|
||||||
uint32_t viz_query_condition,
|
uint32_t viz_query_condition,
|
||||||
uint32_t count_remaining) XE_RESTRICT;
|
uint32_t count_remaining) XE_RESTRICT;
|
||||||
|
|
||||||
bool ExecutePacketType3_DRAW_INDX( uint32_t packet,
|
bool ExecutePacketType3_DRAW_INDX(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
bool ExecutePacketType3_DRAW_INDX_2(uint32_t packet,
|
bool ExecutePacketType3_DRAW_INDX_2(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
@ -87,12 +76,10 @@ XE_FORCEINLINE
|
||||||
bool ExecutePacketType3_LOAD_ALU_CONSTANT(uint32_t packet,
|
bool ExecutePacketType3_LOAD_ALU_CONSTANT(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
|
||||||
bool ExecutePacketType3_SET_SHADER_CONSTANTS(
|
bool ExecutePacketType3_SET_SHADER_CONSTANTS(uint32_t packet,
|
||||||
uint32_t packet,
|
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
|
||||||
bool ExecutePacketType3_IM_LOAD( uint32_t packet,
|
bool ExecutePacketType3_IM_LOAD(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
bool ExecutePacketType3_IM_LOAD_IMMEDIATE(uint32_t packet,
|
bool ExecutePacketType3_IM_LOAD_IMMEDIATE(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
@ -100,9 +87,7 @@ bool ExecutePacketType3_IM_LOAD_IMMEDIATE( uint32_t packet,
|
||||||
bool ExecutePacketType3_INVALIDATE_STATE(uint32_t packet,
|
bool ExecutePacketType3_INVALIDATE_STATE(uint32_t packet,
|
||||||
uint32_t count) XE_RESTRICT;
|
uint32_t count) XE_RESTRICT;
|
||||||
|
|
||||||
bool ExecutePacketType3_VIZ_QUERY( uint32_t packet,
|
bool ExecutePacketType3_VIZ_QUERY(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||||
uint32_t count) XE_RESTRICT;
|
|
||||||
|
|
||||||
|
|
||||||
XE_FORCEINLINE
|
XE_FORCEINLINE
|
||||||
void WriteEventInitiator(uint32_t value) XE_RESTRICT;
|
void WriteEventInitiator(uint32_t value) XE_RESTRICT;
|
||||||
|
|
|
@ -683,11 +683,10 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_INDIRECT_BUFFER(
|
||||||
to 54 bytes
|
to 54 bytes
|
||||||
*/
|
*/
|
||||||
static bool MatchValueAndRef(uint32_t value, uint32_t ref, uint32_t wait_info) {
|
static bool MatchValueAndRef(uint32_t value, uint32_t ref, uint32_t wait_info) {
|
||||||
// smaller code is generated than the #else path, although whether it is faster
|
// smaller code is generated than the #else path, although whether it is
|
||||||
// i do not know. i don't think games do an enormous number of cond_write
|
// faster i do not know. i don't think games do an enormous number of
|
||||||
// though, so we have picked
|
// cond_write though, so we have picked the path with the smaller codegen. we
|
||||||
// the path with the smaller codegen.
|
// do technically have more instructions executed vs the switch case method,
|
||||||
// we do technically have more instructions executed vs the switch case method,
|
|
||||||
// but we have no mispredicts and most of our instructions are 0.25/0.3
|
// but we have no mispredicts and most of our instructions are 0.25/0.3
|
||||||
// throughput
|
// throughput
|
||||||
return ((((value < ref) << 1) | ((value <= ref) << 2) |
|
return ((((value < ref) << 1) | ((value <= ref) << 2) |
|
||||||
|
@ -899,12 +898,16 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE_SHD(
|
||||||
data_value = GpuSwap(data_value, endianness);
|
data_value = GpuSwap(data_value, endianness);
|
||||||
uint8_t* write_destination = memory_->TranslatePhysical(address);
|
uint8_t* write_destination = memory_->TranslatePhysical(address);
|
||||||
if (address > 0x1FFFFFFF) {
|
if (address > 0x1FFFFFFF) {
|
||||||
uint32_t writeback_base = register_file_->values[XE_GPU_REG_WRITEBACK_BASE].u32;
|
uint32_t writeback_base =
|
||||||
uint32_t writeback_size = register_file_->values[XE_GPU_REG_WRITEBACK_SIZE].u32;
|
register_file_->values[XE_GPU_REG_WRITEBACK_BASE].u32;
|
||||||
|
uint32_t writeback_size =
|
||||||
|
register_file_->values[XE_GPU_REG_WRITEBACK_SIZE].u32;
|
||||||
uint32_t writeback_offset = address - writeback_base;
|
uint32_t writeback_offset = address - writeback_base;
|
||||||
//check whether the guest has written writeback base. if they haven't, skip the offset check
|
// check whether the guest has written writeback base. if they haven't, skip
|
||||||
|
// the offset check
|
||||||
if (writeback_base != 0 && writeback_offset < writeback_size) {
|
if (writeback_base != 0 && writeback_offset < writeback_size) {
|
||||||
write_destination = memory_->TranslateVirtual(0x7F000000 + writeback_offset);
|
write_destination =
|
||||||
|
memory_->TranslateVirtual(0x7F000000 + writeback_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xe::store(write_destination, data_value);
|
xe::store(write_destination, data_value);
|
||||||
|
|
|
@ -883,8 +883,7 @@ class PrimitiveProcessor {
|
||||||
// Must be called in a global critical region.
|
// Must be called in a global critical region.
|
||||||
void UpdateCacheBucketsNonEmptyL2(
|
void UpdateCacheBucketsNonEmptyL2(
|
||||||
uint32_t bucket_index_div_64,
|
uint32_t bucket_index_div_64,
|
||||||
[[maybe_unused]] const global_unique_lock_type&
|
[[maybe_unused]] const global_unique_lock_type& global_lock) {
|
||||||
global_lock) {
|
|
||||||
uint64_t& cache_buckets_non_empty_l2_ref =
|
uint64_t& cache_buckets_non_empty_l2_ref =
|
||||||
cache_buckets_non_empty_l2_[bucket_index_div_64 >> 6];
|
cache_buckets_non_empty_l2_[bucket_index_div_64 >> 6];
|
||||||
uint64_t cache_buckets_non_empty_l2_bit = uint64_t(1)
|
uint64_t cache_buckets_non_empty_l2_bit = uint64_t(1)
|
||||||
|
|
|
@ -36,16 +36,18 @@ XE_GPU_REGISTER(0x0398, kDword, RBBM_PERFCOUNTER0_HI)
|
||||||
XE_GPU_REGISTER(0x0399, kDword, RBBM_PERFCOUNTER1_LOW)
|
XE_GPU_REGISTER(0x0399, kDword, RBBM_PERFCOUNTER1_LOW)
|
||||||
XE_GPU_REGISTER(0x039A, kDword, RBBM_PERFCOUNTER1_HI)
|
XE_GPU_REGISTER(0x039A, kDword, RBBM_PERFCOUNTER1_HI)
|
||||||
|
|
||||||
//XAM reads this directly and stores it to a struct, have not tracked where it goes from there
|
// XAM reads this directly and stores it to a struct, have not tracked where it
|
||||||
//PM4 command PM4_MEM_WRITE_CNTR is supposed to write this to memory
|
// goes from there PM4 command PM4_MEM_WRITE_CNTR is supposed to write this to
|
||||||
//XE_GPU_REGISTER(0x44b, kDword,CP_PROG_COUNTER )
|
// memory XE_GPU_REGISTER(0x44b, kDword,CP_PROG_COUNTER )
|
||||||
XE_GPU_REGISTER(0x045E, kDword, CALLBACK_ACK)
|
XE_GPU_REGISTER(0x045E, kDword, CALLBACK_ACK)
|
||||||
|
|
||||||
XE_GPU_REGISTER(0x0578, kDword, SCRATCH_REG0) // interrupt sync
|
XE_GPU_REGISTER(0x0578, kDword, SCRATCH_REG0) // interrupt sync
|
||||||
XE_GPU_REGISTER(0x0579, kDword, SCRATCH_REG1) // present interval
|
XE_GPU_REGISTER(0x0579, kDword, SCRATCH_REG1) // present interval
|
||||||
XE_GPU_REGISTER(0x057A, kDword, SCRATCH_REG2)
|
XE_GPU_REGISTER(0x057A, kDword, SCRATCH_REG2)
|
||||||
XE_GPU_REGISTER(0x057B, kDword, SCRATCH_REG3)
|
XE_GPU_REGISTER(0x057B, kDword, SCRATCH_REG3)
|
||||||
XE_GPU_REGISTER(0x057C, kDword, SCRATCH_REG4) //originally this was named CALLBACK_ADDRESS, but that didnt make sense
|
XE_GPU_REGISTER(0x057C, kDword,
|
||||||
|
SCRATCH_REG4) // originally this was named CALLBACK_ADDRESS,
|
||||||
|
// but that didnt make sense
|
||||||
XE_GPU_REGISTER(0x057D, kDword, SCRATCH_REG5)
|
XE_GPU_REGISTER(0x057D, kDword, SCRATCH_REG5)
|
||||||
XE_GPU_REGISTER(0x057E, kDword, SCRATCH_REG6)
|
XE_GPU_REGISTER(0x057E, kDword, SCRATCH_REG6)
|
||||||
XE_GPU_REGISTER(0x057F, kDword, SCRATCH_REG7)
|
XE_GPU_REGISTER(0x057F, kDword, SCRATCH_REG7)
|
||||||
|
@ -56,7 +58,6 @@ XE_GPU_REGISTER(0x05C8, kDword, WAIT_UNTIL)
|
||||||
// seems to have a negative value while the gpu is busy
|
// seems to have a negative value while the gpu is busy
|
||||||
// XE_GPU_REGISTER(0x05D0, kDword, RBBM_STATUS)
|
// XE_GPU_REGISTER(0x05D0, kDword, RBBM_STATUS)
|
||||||
|
|
||||||
|
|
||||||
// update count = 6 bit field, bits 8- 14
|
// update count = 6 bit field, bits 8- 14
|
||||||
// there are several other fields here, they have an unknown purpose
|
// there are several other fields here, they have an unknown purpose
|
||||||
// XE_GPU_REGISTER(0x704, kDword, CP_RB_CNTL)
|
// XE_GPU_REGISTER(0x704, kDword, CP_RB_CNTL)
|
||||||
|
|
|
@ -231,8 +231,7 @@ class RenderTargetCache {
|
||||||
: register_file_(register_file),
|
: register_file_(register_file),
|
||||||
draw_resolution_scale_x_(draw_resolution_scale_x),
|
draw_resolution_scale_x_(draw_resolution_scale_x),
|
||||||
draw_resolution_scale_y_(draw_resolution_scale_y),
|
draw_resolution_scale_y_(draw_resolution_scale_y),
|
||||||
draw_extent_estimator_(register_file, memory, trace_writer)
|
draw_extent_estimator_(register_file, memory, trace_writer) {
|
||||||
{
|
|
||||||
assert_not_zero(draw_resolution_scale_x);
|
assert_not_zero(draw_resolution_scale_x);
|
||||||
assert_not_zero(draw_resolution_scale_y);
|
assert_not_zero(draw_resolution_scale_y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,8 @@ class TextureCache {
|
||||||
// generate a mask of all bits from before the first index, and xor it with
|
// generate a mask of all bits from before the first index, and xor it with
|
||||||
// all bits before the last index this produces a mask covering only the
|
// all bits before the last index this produces a mask covering only the
|
||||||
// bits between first and last
|
// bits between first and last
|
||||||
uint32_t res = ((1U << first_index) - 1) ^ static_cast<uint32_t>((1ULL << (last_index + 1)) - 1ULL);
|
uint32_t res = ((1U << first_index) - 1) ^
|
||||||
|
static_cast<uint32_t>((1ULL << (last_index + 1)) - 1ULL);
|
||||||
// todo: check that this is right
|
// todo: check that this is right
|
||||||
|
|
||||||
texture_bindings_in_sync_ &= ~res;
|
texture_bindings_in_sync_ &= ~res;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/gpu/draw_util.h"
|
#include "xenia/gpu/draw_util.h"
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
#include "xenia/gpu/gpu_flags.h"
|
||||||
|
#include "xenia/gpu/packet_disassembler.h"
|
||||||
#include "xenia/gpu/registers.h"
|
#include "xenia/gpu/registers.h"
|
||||||
#include "xenia/gpu/shader.h"
|
#include "xenia/gpu/shader.h"
|
||||||
#include "xenia/gpu/spirv_shader_translator.h"
|
#include "xenia/gpu/spirv_shader_translator.h"
|
||||||
|
@ -32,7 +33,6 @@
|
||||||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
#include "xenia/gpu/packet_disassembler.h"
|
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/user_module.h"
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
#ifndef XENIA_GPU_XENOS_H_
|
#ifndef XENIA_GPU_XENOS_H_
|
||||||
#define XENIA_GPU_XENOS_H_
|
#define XENIA_GPU_XENOS_H_
|
||||||
|
|
||||||
|
|
||||||
#include "xenia/base/memory.h"
|
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
|
#include "xenia/base/memory.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
|
@ -54,9 +54,7 @@ struct X_FILE_FS_ATTRIBUTE_INFORMATION {
|
||||||
};
|
};
|
||||||
static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16);
|
static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16);
|
||||||
|
|
||||||
enum X_FILE_DEVICE_TYPE : uint32_t {
|
enum X_FILE_DEVICE_TYPE : uint32_t { FILE_DEVICE_UNKNOWN = 0x22 };
|
||||||
FILE_DEVICE_UNKNOWN = 0x22
|
|
||||||
};
|
|
||||||
|
|
||||||
struct X_FILE_FS_DEVICE_INFORMATION {
|
struct X_FILE_FS_DEVICE_INFORMATION {
|
||||||
be<X_FILE_DEVICE_TYPE> device_type;
|
be<X_FILE_DEVICE_TYPE> device_type;
|
||||||
|
|
|
@ -651,7 +651,8 @@ void UserModule::Dump() {
|
||||||
|
|
||||||
for (uint32_t i = 0; i < opt_alternate_title_id->count(); i++) {
|
for (uint32_t i = 0; i < opt_alternate_title_id->count(); i++) {
|
||||||
if (opt_alternate_title_id->values[i] != 0) {
|
if (opt_alternate_title_id->values[i] != 0) {
|
||||||
title_ids.append(fmt::format(" {:08X},", opt_alternate_title_id->values[i]));
|
title_ids.append(
|
||||||
|
fmt::format(" {:08X},", opt_alternate_title_id->values[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove last character as it is not necessary
|
// Remove last character as it is not necessary
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Copyright 2023 Xenia Canary. All rights reserved. *
|
* Copyright 2023 Xenia Canary. All rights reserved. * Released under the BSD
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
*license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -95,7 +95,8 @@ bool GrowHandleTable(uint32_t table_ptr, PPCContext* context) {
|
||||||
/*
|
/*
|
||||||
copy old bucket list contents to new, larger bucket list
|
copy old bucket list contents to new, larger bucket list
|
||||||
*/
|
*/
|
||||||
memcpy(context->TranslateVirtual(new_dynamic_buckets),
|
memcpy(
|
||||||
|
context->TranslateVirtual(new_dynamic_buckets),
|
||||||
context->TranslateVirtual(table->table_dynamic_buckets),
|
context->TranslateVirtual(table->table_dynamic_buckets),
|
||||||
sizeof(uint32_t) * (new_bucket_handle_base / SIZE_PER_HANDLE_BUCKET));
|
sizeof(uint32_t) * (new_bucket_handle_base / SIZE_PER_HANDLE_BUCKET));
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Copyright 2023 Xenia Canary. All rights reserved. *
|
* Copyright 2023 Xenia Canary. All rights reserved. * Released under the BSD
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
*license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,5 @@ struct X_OBJECT_CREATE_INFORMATION;
|
||||||
namespace xe::kernel::util {
|
namespace xe::kernel::util {
|
||||||
class NativeList;
|
class NativeList;
|
||||||
class ObjectTable;
|
class ObjectTable;
|
||||||
}
|
} // namespace xe::kernel::util
|
||||||
#endif
|
#endif
|
|
@ -168,8 +168,8 @@ static void XeInsertHeadList(uint32_t list_head, X_LIST_ENTRY* entry,
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
static void XeInsertHeadList(X_LIST_ENTRY* list_head, X_LIST_ENTRY* entry,
|
static void XeInsertHeadList(X_LIST_ENTRY* list_head, X_LIST_ENTRY* entry,
|
||||||
VirtualTranslator context) {
|
VirtualTranslator context) {
|
||||||
XeInsertHeadList(list_head, XeGuestList(list_head, context),
|
XeInsertHeadList(list_head, XeGuestList(list_head, context), entry,
|
||||||
entry, XeGuestList(entry, context), context);
|
XeGuestList(entry, context), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TObject, size_t EntryListOffset>
|
template <typename TObject, size_t EntryListOffset>
|
||||||
|
@ -227,7 +227,8 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
void InsertHead(TObject* entry, VirtualTranslator translator) {
|
void InsertHead(TObject* entry, VirtualTranslator translator) {
|
||||||
XeInsertHeadList(static_cast<X_LIST_ENTRY*>(this), ObjectListEntry(entry), translator);
|
XeInsertHeadList(static_cast<X_LIST_ENTRY*>(this), ObjectListEntry(entry),
|
||||||
|
translator);
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
void InsertTail(TObject* entry, VirtualTranslator translator) {
|
void InsertTail(TObject* entry, VirtualTranslator translator) {
|
||||||
|
|
|
@ -111,8 +111,8 @@ class ObjectTable {
|
||||||
|
|
||||||
// Generic lookup
|
// Generic lookup
|
||||||
template <>
|
template <>
|
||||||
object_ref<XObject> ObjectTable::LookupObject<XObject>(
|
object_ref<XObject> ObjectTable::LookupObject<XObject>(X_HANDLE handle,
|
||||||
X_HANDLE handle, bool already_locked);
|
bool already_locked);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
|
|
|
@ -35,8 +35,7 @@ using PPCContext = xe::cpu::ppc::PPCContext;
|
||||||
library_name, ordinals::export_name, \
|
library_name, ordinals::export_name, \
|
||||||
(xe::cpu::xe_kernel_export_shim_fn)export_name##_entry);
|
(xe::cpu::xe_kernel_export_shim_fn)export_name##_entry);
|
||||||
|
|
||||||
#define SHIM_MEM_ADDR(a) \
|
#define SHIM_MEM_ADDR(a) ((a) ? ppc_context->TranslateVirtual(a) : nullptr)
|
||||||
((a) ? ppc_context->TranslateVirtual(a) : nullptr)
|
|
||||||
|
|
||||||
#define SHIM_MEM_8(a) xe::load_and_swap<uint8_t>(SHIM_MEM_ADDR(a))
|
#define SHIM_MEM_8(a) xe::load_and_swap<uint8_t>(SHIM_MEM_ADDR(a))
|
||||||
#define SHIM_MEM_16(a) xe::load_and_swap<uint16_t>(SHIM_MEM_ADDR(a))
|
#define SHIM_MEM_16(a) xe::load_and_swap<uint16_t>(SHIM_MEM_ADDR(a))
|
||||||
|
@ -158,9 +157,8 @@ class Param {
|
||||||
} else {
|
} else {
|
||||||
uint32_t stack_ptr =
|
uint32_t stack_ptr =
|
||||||
uint32_t(init.ppc_context->r[1]) + 0x54 + (ordinal_ - 8) * 8;
|
uint32_t(init.ppc_context->r[1]) + 0x54 + (ordinal_ - 8) * 8;
|
||||||
*out_value = xe::load_and_swap<V>(
|
*out_value =
|
||||||
init.ppc_context->TranslateVirtual(
|
xe::load_and_swap<V>(init.ppc_context->TranslateVirtual(stack_ptr));
|
||||||
stack_ptr));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +214,7 @@ class ContextParam : public Param {
|
||||||
X_KPCR* GetPCR() const { return TranslateGPR<X_KPCR*>(13); }
|
X_KPCR* GetPCR() const { return TranslateGPR<X_KPCR*>(13); }
|
||||||
|
|
||||||
XThread* CurrentXThread() const;
|
XThread* CurrentXThread() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PPCContext* XE_RESTRICT ctx_;
|
PPCContext* XE_RESTRICT ctx_;
|
||||||
};
|
};
|
||||||
|
@ -223,10 +222,7 @@ class ContextParam : public Param {
|
||||||
class PointerParam : public ParamBase<uint32_t> {
|
class PointerParam : public ParamBase<uint32_t> {
|
||||||
public:
|
public:
|
||||||
PointerParam(Init& init) : ParamBase(init) {
|
PointerParam(Init& init) : ParamBase(init) {
|
||||||
host_ptr_ =
|
host_ptr_ = value_ ? init.ppc_context->TranslateVirtual(value_) : nullptr;
|
||||||
value_
|
|
||||||
? init.ppc_context->TranslateVirtual(value_)
|
|
||||||
: nullptr;
|
|
||||||
}
|
}
|
||||||
PointerParam(void* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
PointerParam(void* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||||
PointerParam& operator=(void*& other) {
|
PointerParam& operator=(void*& other) {
|
||||||
|
@ -296,10 +292,7 @@ class StringPointerParam : public ParamBase<uint32_t> {
|
||||||
public:
|
public:
|
||||||
StringPointerParam(Init& init) : ParamBase(init) {
|
StringPointerParam(Init& init) : ParamBase(init) {
|
||||||
host_ptr_ =
|
host_ptr_ =
|
||||||
value_
|
value_ ? init.ppc_context->TranslateVirtual<CHAR*>(value_) : nullptr;
|
||||||
? init.ppc_context->TranslateVirtual<CHAR*>(
|
|
||||||
value_)
|
|
||||||
: nullptr;
|
|
||||||
}
|
}
|
||||||
StringPointerParam(CHAR* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
StringPointerParam(CHAR* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||||
StringPointerParam& operator=(const CHAR*& other) {
|
StringPointerParam& operator=(const CHAR*& other) {
|
||||||
|
@ -323,9 +316,7 @@ class TypedPointerParam : public ParamBase<uint32_t> {
|
||||||
public:
|
public:
|
||||||
TypedPointerParam(Init& init) : ParamBase(init) {
|
TypedPointerParam(Init& init) : ParamBase(init) {
|
||||||
host_ptr_ =
|
host_ptr_ =
|
||||||
value_ ? init.ppc_context->TranslateVirtual<T*>(
|
value_ ? init.ppc_context->TranslateVirtual<T*>(value_) : nullptr;
|
||||||
value_)
|
|
||||||
: nullptr;
|
|
||||||
}
|
}
|
||||||
TypedPointerParam(T* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
TypedPointerParam(T* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||||
TypedPointerParam& operator=(const T*& other) {
|
TypedPointerParam& operator=(const T*& other) {
|
||||||
|
|
|
@ -187,8 +187,7 @@ class XdbfWrapper {
|
||||||
XdbfPropertyTableEntry GetProperty(const uint32_t id) const;
|
XdbfPropertyTableEntry GetProperty(const uint32_t id) const;
|
||||||
XdbfContextTableEntry GetContext(const uint32_t id) const;
|
XdbfContextTableEntry GetContext(const uint32_t id) const;
|
||||||
std::vector<XdbfViewTable> GetStatsView() const;
|
std::vector<XdbfViewTable> GetStatsView() const;
|
||||||
XdbfSharedView GetSharedView(const uint8_t* ptr,
|
XdbfSharedView GetSharedView(const uint8_t* ptr, uint32_t& byte_count) const;
|
||||||
uint32_t& byte_count) const;
|
|
||||||
|
|
||||||
void GetPropertyBagMetadata(const uint8_t* ptr, uint32_t& byte_count,
|
void GetPropertyBagMetadata(const uint8_t* ptr, uint32_t& byte_count,
|
||||||
std::vector<xe::be<uint32_t>>& contexts,
|
std::vector<xe::be<uint32_t>>& contexts,
|
||||||
|
@ -196,7 +195,8 @@ class XdbfWrapper {
|
||||||
|
|
||||||
XdbfPropertyBag GetMatchCollection() const;
|
XdbfPropertyBag GetMatchCollection() const;
|
||||||
|
|
||||||
const uint8_t* ReadXLast(uint32_t& compressed_size, uint32_t& decompressed_size) const;
|
const uint8_t* ReadXLast(uint32_t& compressed_size,
|
||||||
|
uint32_t& decompressed_size) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint8_t* data_ = nullptr;
|
const uint8_t* data_ = nullptr;
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
#include "xenia/kernel/xam/content_manager.h"
|
#include "xenia/kernel/xam/content_manager.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
|
|
|
@ -33,8 +33,7 @@
|
||||||
|
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
|
|
||||||
DEFINE_int32(
|
DEFINE_int32(avpack, 8,
|
||||||
avpack, 8,
|
|
||||||
"Video modes\n"
|
"Video modes\n"
|
||||||
" 0 = PAL-60 Component (SD)\n"
|
" 0 = PAL-60 Component (SD)\n"
|
||||||
" 1 = Unused\n"
|
" 1 = Unused\n"
|
||||||
|
|
|
@ -431,7 +431,8 @@ dword_result_t XamGetLocaleEx_entry(dword_t max_country_id,
|
||||||
static_cast<uint8_t>(max_locale_id));
|
static_cast<uint8_t>(max_locale_id));
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamGetLocaleEx, kLocale, kImplemented);
|
DECLARE_XAM_EXPORT1(XamGetLocaleEx, kLocale, kImplemented);
|
||||||
//originally a switch table, wrote a script to extract the values for all possible cases
|
// originally a switch table, wrote a script to extract the values for all
|
||||||
|
// possible cases
|
||||||
|
|
||||||
static constexpr uint8_t XamLocaleDateFmtTable[] = {
|
static constexpr uint8_t XamLocaleDateFmtTable[] = {
|
||||||
2, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 1, 4, 2, 3, 1, 2, 2, 3,
|
2, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 1, 4, 2, 3, 1, 2, 2, 3,
|
||||||
|
|
|
@ -66,9 +66,9 @@ dword_result_t XamTaskSchedule_entry(lpvoid_t callback,
|
||||||
// Stack must be aligned to 16kb pages
|
// Stack must be aligned to 16kb pages
|
||||||
stack_size = std::max((uint32_t)0x4000, ((stack_size + 0xFFF) & 0xFFFFF000));
|
stack_size = std::max((uint32_t)0x4000, ((stack_size + 0xFFF) & 0xFFFFF000));
|
||||||
|
|
||||||
auto thread =
|
auto thread = object_ref<XThread>(new XThread(
|
||||||
object_ref<XThread>(new XThread(kernel_state(), stack_size, 0, callback,
|
kernel_state(), stack_size, 0, callback, message.guest_address(), 0, true,
|
||||||
message.guest_address(), 0, true, false, kernel_state()->GetSystemProcess()));
|
false, kernel_state()->GetSystemProcess()));
|
||||||
|
|
||||||
X_STATUS result = thread->Create();
|
X_STATUS result = thread->Create();
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,8 @@ void RtlRaiseException_entry(pointer_t<X_EXCEPTION_RECORD> record) {
|
||||||
// This is going to suck.
|
// This is going to suck.
|
||||||
// xe::debugging::Break();
|
// xe::debugging::Break();
|
||||||
|
|
||||||
//RtlRaiseException definitely wasn't a noreturn function, we can return safe-ish
|
// RtlRaiseException definitely wasn't a noreturn function, we can return
|
||||||
|
// safe-ish
|
||||||
XELOGE("Guest attempted to trigger a breakpoint!");
|
XELOGE("Guest attempted to trigger a breakpoint!");
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT2(RtlRaiseException, kDebug, kStub, kImportant);
|
DECLARE_XBOXKRNL_EXPORT2(RtlRaiseException, kDebug, kStub, kImportant);
|
||||||
|
|
|
@ -723,8 +723,9 @@ dword_result_t IoCreateDevice_entry(dword_t driver_object,
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(IoCreateDevice, kFileSystem, kStub);
|
DECLARE_XBOXKRNL_EXPORT1(IoCreateDevice, kFileSystem, kStub);
|
||||||
|
|
||||||
//supposed to invoke a callback on the driver object! its some sort of destructor function
|
// supposed to invoke a callback on the driver object! its some sort of
|
||||||
//intended to be called for all devices created from the driver
|
// destructor function intended to be called for all devices created from the
|
||||||
|
// driver
|
||||||
void IoDeleteDevice_entry(dword_t device_ptr, const ppc_context_t& ctx) {
|
void IoDeleteDevice_entry(dword_t device_ptr, const ppc_context_t& ctx) {
|
||||||
if (device_ptr) {
|
if (device_ptr) {
|
||||||
auto kernel_mem = ctx->kernel_state->memory();
|
auto kernel_mem = ctx->kernel_state->memory();
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
|
||||||
namespace xboxkrnl {
|
namespace xboxkrnl {
|
||||||
|
|
||||||
uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size,
|
uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size,
|
||||||
|
|
|
@ -434,8 +434,10 @@ pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) {
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented);
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-imagedirectoryentrytodata
|
// https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-imagedirectoryentrytodata
|
||||||
dword_result_t RtlImageDirectoryEntryToData_entry(dword_t Base, dword_t MappedAsImage_,
|
dword_result_t RtlImageDirectoryEntryToData_entry(dword_t Base,
|
||||||
word_t DirectoryEntry, dword_t Size,
|
dword_t MappedAsImage_,
|
||||||
|
word_t DirectoryEntry,
|
||||||
|
dword_t Size,
|
||||||
const ppc_context_t& ctx) {
|
const ppc_context_t& ctx) {
|
||||||
bool MappedAsImage = static_cast<unsigned char>(MappedAsImage_);
|
bool MappedAsImage = static_cast<unsigned char>(MappedAsImage_);
|
||||||
uint32_t aligned_base = Base;
|
uint32_t aligned_base = Base;
|
||||||
|
|
|
@ -63,7 +63,8 @@ uint32_t xeNtQueueApcThread(uint32_t thread_handle, uint32_t apc_routine,
|
||||||
void xeKfLowerIrql(PPCContext* ctx, unsigned char new_irql);
|
void xeKfLowerIrql(PPCContext* ctx, unsigned char new_irql);
|
||||||
unsigned char xeKfRaiseIrql(PPCContext* ctx, unsigned char new_irql);
|
unsigned char xeKfRaiseIrql(PPCContext* ctx, unsigned char new_irql);
|
||||||
|
|
||||||
void xeKeKfReleaseSpinLock(PPCContext* ctx, X_KSPINLOCK* lock, uint32_t old_irql, bool change_irql=true);
|
void xeKeKfReleaseSpinLock(PPCContext* ctx, X_KSPINLOCK* lock,
|
||||||
|
uint32_t old_irql, bool change_irql = true);
|
||||||
uint32_t xeKeKfAcquireSpinLock(PPCContext* ctx, X_KSPINLOCK* lock,
|
uint32_t xeKeKfAcquireSpinLock(PPCContext* ctx, X_KSPINLOCK* lock,
|
||||||
bool change_irql = true);
|
bool change_irql = true);
|
||||||
|
|
||||||
|
|
|
@ -388,7 +388,9 @@ object_ref<XObject> XObject::GetNativeObject(KernelState* kernel_state,
|
||||||
// Already initialized.
|
// Already initialized.
|
||||||
// TODO: assert if the type of the object != as_type
|
// TODO: assert if the type of the object != as_type
|
||||||
uint32_t handle = header->wait_list_blink;
|
uint32_t handle = header->wait_list_blink;
|
||||||
result = kernel_state->object_table()->LookupObject<XObject>(handle, true).release();
|
result = kernel_state->object_table()
|
||||||
|
->LookupObject<XObject>(handle, true)
|
||||||
|
.release();
|
||||||
} else {
|
} else {
|
||||||
// First use, create new.
|
// First use, create new.
|
||||||
// https://www.nirsoft.net/kernel_struct/vista/KOBJECTS.html
|
// https://www.nirsoft.net/kernel_struct/vista/KOBJECTS.html
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
|
|
@ -358,6 +358,7 @@ class XThread : public XObject, public cpu::Thread {
|
||||||
pending_mutant_acquires_.push_back(mutant);
|
pending_mutant_acquires_.push_back(mutant);
|
||||||
}
|
}
|
||||||
void SetCurrentThread();
|
void SetCurrentThread();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool AllocateStack(uint32_t size);
|
bool AllocateStack(uint32_t size);
|
||||||
void FreeStack();
|
void FreeStack();
|
||||||
|
@ -391,7 +392,8 @@ class XThread : public XObject, public cpu::Thread {
|
||||||
class XHostThread : public XThread {
|
class XHostThread : public XThread {
|
||||||
public:
|
public:
|
||||||
XHostThread(KernelState* kernel_state, uint32_t stack_size,
|
XHostThread(KernelState* kernel_state, uint32_t stack_size,
|
||||||
uint32_t creation_flags, std::function<int()> host_fn, uint32_t guest_process=0);
|
uint32_t creation_flags, std::function<int()> host_fn,
|
||||||
|
uint32_t guest_process = 0);
|
||||||
|
|
||||||
virtual void Execute();
|
virtual void Execute();
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
|
|
||||||
#include "xenia/cpu/mmio_handler.h"
|
#include "xenia/cpu/mmio_handler.h"
|
||||||
|
|
||||||
// TODO(benvanik): move xbox.h out
|
// TODO(benvanik): move xbox.h out
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
*/
|
*/
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#include "xenia/config.h"
|
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/config.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
#include "xenia/patcher/patch_db.h"
|
#include "xenia/patcher/patch_db.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
static const uint8_t player_one_notification_icon[] = {
|
static const uint8_t player_one_notification_icon[] = {
|
||||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
@ -4269,8 +4269,8 @@ static const uint8_t player_any_notification_icon[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
|
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
|
||||||
static const uint32_t player_any_notification_icon_len = 10320;
|
static const uint32_t player_any_notification_icon_len = 10320;
|
||||||
|
|
||||||
|
static const std::vector<std::pair<const uint8_t*, uint32_t>>
|
||||||
static const std::vector<std::pair<const uint8_t*, uint32_t>> notification_icons = {
|
notification_icons = {
|
||||||
{player_one_notification_icon, player_one_notification_icon_len},
|
{player_one_notification_icon, player_one_notification_icon_len},
|
||||||
{player_two_notification_icon, player_two_notification_icon_len},
|
{player_two_notification_icon, player_two_notification_icon_len},
|
||||||
{player_three_notification_icon, player_three_notification_icon_len},
|
{player_three_notification_icon, player_three_notification_icon_len},
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/string_util.h"
|
#include "xenia/base/string_util.h"
|
||||||
#include "xenia/kernel/util/xex2_info.h"
|
#include "xenia/kernel/util/xex2_info.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace vfs {
|
namespace vfs {
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
|
||||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
||||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_device.h"
|
#include "xenia/vfs/devices/xcontent_devices/svod_container_device.h"
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/kernel/util/xex2_info.h"
|
#include "xenia/kernel/util/xex2_info.h"
|
||||||
|
#include "xenia/kernel/xam/content_manager.h"
|
||||||
#include "xenia/vfs/device.h"
|
#include "xenia/vfs/device.h"
|
||||||
#include "xenia/vfs/devices/stfs_xbox.h"
|
#include "xenia/vfs/devices/stfs_xbox.h"
|
||||||
#include "xenia/kernel/xam/content_manager.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace vfs {
|
namespace vfs {
|
||||||
|
@ -71,7 +71,8 @@ class XContentContainerDevice : public Device {
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Result Read() = 0;
|
virtual Result Read() = 0;
|
||||||
// Load all host files. Usually STFS is only 1 file, meanwhile SVOD is usually multiple file.
|
// Load all host files. Usually STFS is only 1 file, meanwhile SVOD is usually
|
||||||
|
// multiple file.
|
||||||
virtual Result LoadHostFiles(FILE* header_file) = 0;
|
virtual Result LoadHostFiles(FILE* header_file) = 0;
|
||||||
// Initialize any container specific fields.
|
// Initialize any container specific fields.
|
||||||
virtual void SetupContainer(){};
|
virtual void SetupContainer(){};
|
||||||
|
@ -89,7 +90,9 @@ class XContentContainerDevice : public Device {
|
||||||
|
|
||||||
const std::filesystem::path& GetHostPath() const { return host_path_; }
|
const std::filesystem::path& GetHostPath() const { return host_path_; }
|
||||||
|
|
||||||
const XContentContainerHeader* GetContainerHeader() const { return header_.get(); }
|
const XContentContainerHeader* GetContainerHeader() const {
|
||||||
|
return header_.get();
|
||||||
|
}
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::filesystem::path host_path_;
|
std::filesystem::path host_path_;
|
||||||
|
|
|
@ -312,7 +312,6 @@ struct X_EX_TITLE_TERMINATE_REGISTRATION {
|
||||||
};
|
};
|
||||||
static_assert_size(X_EX_TITLE_TERMINATE_REGISTRATION, 16);
|
static_assert_size(X_EX_TITLE_TERMINATE_REGISTRATION, 16);
|
||||||
|
|
||||||
|
|
||||||
enum X_OBJECT_HEADER_FLAGS : uint16_t {
|
enum X_OBJECT_HEADER_FLAGS : uint16_t {
|
||||||
OBJECT_HEADER_FLAG_NAMED_OBJECT =
|
OBJECT_HEADER_FLAG_NAMED_OBJECT =
|
||||||
1, // if set, has X_OBJECT_HEADER_NAME_INFO prior to X_OBJECT_HEADER
|
1, // if set, has X_OBJECT_HEADER_NAME_INFO prior to X_OBJECT_HEADER
|
||||||
|
|
|
@ -2,120 +2,192 @@
|
||||||
#
|
#
|
||||||
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
|
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
|
||||||
#
|
#
|
||||||
# The LLVM Compiler Infrastructure
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
#
|
# See https://llvm.org/LICENSE.txt for license information.
|
||||||
# This file is distributed under the University of Illinois Open Source
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
# License. See LICENSE.TXT for details.
|
|
||||||
#
|
#
|
||||||
# ===------------------------------------------------------------------------===#
|
# ===------------------------------------------------------------------------===#
|
||||||
|
|
||||||
r"""
|
"""
|
||||||
ClangFormat Diff Reformatter
|
|
||||||
============================
|
|
||||||
|
|
||||||
This script reads input from a unified diff and reformats all the changed
|
This script reads input from a unified diff and reformats all the changed
|
||||||
lines. This is useful to reformat all the lines touched by a specific patch.
|
lines. This is useful to reformat all the lines touched by a specific patch.
|
||||||
Example usage for git/svn users:
|
Example usage for git/svn users:
|
||||||
|
|
||||||
git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i
|
git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i
|
||||||
svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i
|
svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i
|
||||||
|
|
||||||
|
It should be noted that the filename contained in the diff is used unmodified
|
||||||
|
to determine the source file to update. Users calling this script directly
|
||||||
|
should be careful to ensure that the path in the diff is correct relative to the
|
||||||
|
current working directory.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import difflib
|
import difflib
|
||||||
import re
|
import re
|
||||||
import string
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import StringIO
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info.major >= 3:
|
||||||
|
from io import StringIO
|
||||||
|
else:
|
||||||
|
from io import BytesIO as StringIO
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=
|
parser = argparse.ArgumentParser(
|
||||||
'Reformat changed lines in diff. Without -i '
|
description=__doc__.format(clang_format_diff="%(prog)s"),
|
||||||
'option just output the diff that would be '
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
'introduced.')
|
)
|
||||||
parser.add_argument('-i', action='store_true', default=False,
|
parser.add_argument(
|
||||||
help='apply edits to files instead of displaying a diff')
|
"-i",
|
||||||
parser.add_argument('-p', metavar='NUM', default=0,
|
action="store_true",
|
||||||
help='strip the smallest prefix containing P slashes')
|
default=False,
|
||||||
parser.add_argument('-regex', metavar='PATTERN', default=None,
|
help="apply edits to files instead of displaying a diff",
|
||||||
help='custom pattern selecting file paths to reformat '
|
)
|
||||||
'(case sensitive, overrides -iregex)')
|
parser.add_argument(
|
||||||
parser.add_argument('-iregex', metavar='PATTERN', default=
|
"-p",
|
||||||
r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto'
|
metavar="NUM",
|
||||||
r'|protodevel|java)',
|
default=0,
|
||||||
help='custom pattern selecting file paths to reformat '
|
help="strip the smallest prefix containing P slashes",
|
||||||
'(case insensitive, overridden by -regex)')
|
)
|
||||||
parser.add_argument('-sort-includes', action='store_true', default=False,
|
parser.add_argument(
|
||||||
help='let clang-format sort include blocks')
|
"-regex",
|
||||||
parser.add_argument('-v', '--verbose', action='store_true',
|
metavar="PATTERN",
|
||||||
help='be more verbose, ineffective without -i')
|
default=None,
|
||||||
parser.add_argument('-style',
|
help="custom pattern selecting file paths to reformat "
|
||||||
help='formatting style to apply (LLVM, Google, Chromium, '
|
"(case sensitive, overrides -iregex)",
|
||||||
'Mozilla, WebKit)')
|
)
|
||||||
parser.add_argument('-binary', default='clang-format',
|
parser.add_argument(
|
||||||
help='location of binary to use for clang-format')
|
"-iregex",
|
||||||
|
metavar="PATTERN",
|
||||||
|
default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp"
|
||||||
|
r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|s?vh?)",
|
||||||
|
help="custom pattern selecting file paths to reformat "
|
||||||
|
"(case insensitive, overridden by -regex)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-sort-includes",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="let clang-format sort include blocks",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
action="store_true",
|
||||||
|
help="be more verbose, ineffective without -i",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-style",
|
||||||
|
help="formatting style to apply (LLVM, GNU, Google, Chromium, "
|
||||||
|
"Microsoft, Mozilla, WebKit)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-fallback-style",
|
||||||
|
help="The name of the predefined style used as a"
|
||||||
|
"fallback in case clang-format is invoked with"
|
||||||
|
"-style=file, but can not find the .clang-format"
|
||||||
|
"file to use.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-binary",
|
||||||
|
default="clang-format",
|
||||||
|
help="location of binary to use for clang-format",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Extract changed lines for each file.
|
# Extract changed lines for each file.
|
||||||
filename = None
|
filename = None
|
||||||
lines_by_file = {}
|
lines_by_file = {}
|
||||||
for line in sys.stdin:
|
for line in sys.stdin:
|
||||||
match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
|
match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line)
|
||||||
if match:
|
if match:
|
||||||
filename = match.group(2)
|
filename = match.group(2)
|
||||||
if filename == None:
|
if filename is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if args.regex is not None:
|
if args.regex is not None:
|
||||||
if not re.match('^%s$' % args.regex, filename):
|
if not re.match("^%s$" % args.regex, filename):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
|
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
|
match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line)
|
||||||
if match:
|
if match:
|
||||||
start_line = int(match.group(1))
|
start_line = int(match.group(1))
|
||||||
line_count = 1
|
line_count = 1
|
||||||
if match.group(3):
|
if match.group(2):
|
||||||
line_count = int(match.group(3))
|
line_count = int(match.group(2))
|
||||||
|
# The input is something like
|
||||||
|
#
|
||||||
|
# @@ -1, +0,0 @@
|
||||||
|
#
|
||||||
|
# which means no lines were added.
|
||||||
if line_count == 0:
|
if line_count == 0:
|
||||||
continue
|
continue
|
||||||
end_line = start_line + line_count - 1;
|
# Also format lines range if line_count is 0 in case of deleting
|
||||||
|
# surrounding statements.
|
||||||
|
end_line = start_line
|
||||||
|
if line_count != 0:
|
||||||
|
end_line += line_count - 1
|
||||||
lines_by_file.setdefault(filename, []).extend(
|
lines_by_file.setdefault(filename, []).extend(
|
||||||
['-lines', str(start_line) + ':' + str(end_line)])
|
["-lines", str(start_line) + ":" + str(end_line)]
|
||||||
|
)
|
||||||
|
|
||||||
# Reformat files containing changes in place.
|
# Reformat files containing changes in place.
|
||||||
for filename, lines in lines_by_file.iteritems():
|
for filename, lines in lines_by_file.items():
|
||||||
if args.i and args.verbose:
|
if args.i and args.verbose:
|
||||||
print 'Formatting', filename
|
print("Formatting {}".format(filename))
|
||||||
command = [args.binary, filename]
|
command = [args.binary, filename]
|
||||||
if args.i:
|
if args.i:
|
||||||
command.append('-i')
|
command.append("-i")
|
||||||
if args.sort_includes:
|
if args.sort_includes:
|
||||||
command.append('-sort-includes')
|
command.append("-sort-includes")
|
||||||
command.extend(lines)
|
command.extend(lines)
|
||||||
if args.style:
|
if args.style:
|
||||||
command.extend(['-style', args.style])
|
command.extend(["-style", args.style])
|
||||||
p = subprocess.Popen(command, stdout=subprocess.PIPE,
|
if args.fallback_style:
|
||||||
stderr=None, stdin=subprocess.PIPE)
|
command.extend(["-fallback-style", args.fallback_style])
|
||||||
|
|
||||||
|
try:
|
||||||
|
p = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=None,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
universal_newlines=True,
|
||||||
|
)
|
||||||
|
except OSError as e:
|
||||||
|
# Give the user more context when clang-format isn't
|
||||||
|
# found/isn't executable, etc.
|
||||||
|
raise RuntimeError(
|
||||||
|
'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)
|
||||||
|
)
|
||||||
|
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
sys.exit(p.returncode);
|
sys.exit(p.returncode)
|
||||||
|
|
||||||
if not args.i:
|
if not args.i:
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
code = f.readlines()
|
code = f.readlines()
|
||||||
formatted_code = StringIO.StringIO(stdout).readlines()
|
formatted_code = StringIO(stdout).readlines()
|
||||||
diff = difflib.unified_diff(code, formatted_code,
|
diff = difflib.unified_diff(
|
||||||
filename, filename,
|
code,
|
||||||
'(before formatting)', '(after formatting)')
|
formatted_code,
|
||||||
diff_string = string.join(diff, '')
|
filename,
|
||||||
|
filename,
|
||||||
|
"(before formatting)",
|
||||||
|
"(after formatting)",
|
||||||
|
)
|
||||||
|
diff_string = "".join(diff)
|
||||||
if len(diff_string) > 0:
|
if len(diff_string) > 0:
|
||||||
sys.stdout.write(diff_string)
|
sys.stdout.write(diff_string)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -12,47 +12,62 @@
|
||||||
# It operates on the current, potentially unsaved buffer and does not create
|
# It operates on the current, potentially unsaved buffer and does not create
|
||||||
# or save any files. To revert a formatting, just undo.
|
# or save any files. To revert a formatting, just undo.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
import sublime
|
import sublime
|
||||||
import sublime_plugin
|
import sublime_plugin
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
# Change this to the full path if clang-format is not on the path.
|
# Change this to the full path if clang-format is not on the path.
|
||||||
binary = 'clang-format'
|
binary = "clang-format"
|
||||||
|
|
||||||
# Change this to format according to other formatting styles. See the output of
|
# Change this to format according to other formatting styles. See the output of
|
||||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||||
# used.
|
# used.
|
||||||
style = 'file'
|
style = None
|
||||||
|
|
||||||
|
|
||||||
class ClangFormatCommand(sublime_plugin.TextCommand):
|
class ClangFormatCommand(sublime_plugin.TextCommand):
|
||||||
def run(self, edit):
|
def run(self, edit):
|
||||||
encoding = self.view.encoding()
|
encoding = self.view.encoding()
|
||||||
if encoding == 'Undefined':
|
if encoding == "Undefined":
|
||||||
encoding = 'utf-8'
|
encoding = "utf-8"
|
||||||
regions = []
|
regions = []
|
||||||
command = [binary, '-style', style]
|
command = [binary]
|
||||||
|
if style:
|
||||||
|
command.extend(["-style", style])
|
||||||
for region in self.view.sel():
|
for region in self.view.sel():
|
||||||
regions.append(region)
|
regions.append(region)
|
||||||
region_offset = min(region.a, region.b)
|
region_offset = min(region.a, region.b)
|
||||||
region_length = abs(region.b - region.a)
|
region_length = abs(region.b - region.a)
|
||||||
command.extend(['-offset', str(region_offset),
|
command.extend(
|
||||||
'-length', str(region_length),
|
[
|
||||||
'-assume-filename', str(self.view.file_name())])
|
"-offset",
|
||||||
|
str(region_offset),
|
||||||
|
"-length",
|
||||||
|
str(region_length),
|
||||||
|
"-assume-filename",
|
||||||
|
str(self.view.file_name()),
|
||||||
|
]
|
||||||
|
)
|
||||||
old_viewport_position = self.view.viewport_position()
|
old_viewport_position = self.view.viewport_position()
|
||||||
buf = self.view.substr(sublime.Region(0, self.view.size()))
|
buf = self.view.substr(sublime.Region(0, self.view.size()))
|
||||||
p = subprocess.Popen(command, stdout=subprocess.PIPE,
|
p = subprocess.Popen(
|
||||||
stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
)
|
||||||
output, error = p.communicate(buf.encode(encoding))
|
output, error = p.communicate(buf.encode(encoding))
|
||||||
if error:
|
if error:
|
||||||
print(error)
|
print(error)
|
||||||
self.view.replace(
|
self.view.replace(
|
||||||
edit, sublime.Region(0, self.view.size()),
|
edit, sublime.Region(0, self.view.size()), output.decode(encoding)
|
||||||
output.decode(encoding))
|
)
|
||||||
self.view.sel().clear()
|
self.view.sel().clear()
|
||||||
for region in regions:
|
for region in regions:
|
||||||
self.view.sel().add(region)
|
self.view.sel().add(region)
|
||||||
# FIXME: Without the 10ms delay, the viewport sometimes jumps.
|
# FIXME: Without the 10ms delay, the viewport sometimes jumps.
|
||||||
sublime.set_timeout(lambda: self.view.set_viewport_position(
|
sublime.set_timeout(
|
||||||
old_viewport_position, False), 10)
|
lambda: self.view.set_viewport_position(old_viewport_position, False), 10
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
;;; clang-format.el --- Format code using clang-format -*- lexical-binding: t; -*-
|
;;; clang-format.el --- Format code using clang-format -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Version: 0.1.0
|
||||||
;; Keywords: tools, c
|
;; Keywords: tools, c
|
||||||
;; Package-Requires: ((cl-lib "0.3"))
|
;; Package-Requires: ((cl-lib "0.3"))
|
||||||
|
;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
|
@ -45,17 +47,29 @@ A string containing the name or the full path of the executable."
|
||||||
:type '(file :must-match t)
|
:type '(file :must-match t)
|
||||||
:risky t)
|
:risky t)
|
||||||
|
|
||||||
(defcustom clang-format-style "file"
|
(defcustom clang-format-style nil
|
||||||
"Style argument to pass to clang-format.
|
"Style argument to pass to clang-format.
|
||||||
|
|
||||||
By default clang-format will load the style configuration from
|
By default clang-format will load the style configuration from
|
||||||
a file named .clang-format located in one of the parent directories
|
a file named .clang-format located in one of the parent directories
|
||||||
of the buffer."
|
of the buffer."
|
||||||
:group 'clang-format
|
:group 'clang-format
|
||||||
:type 'string
|
:type '(choice (string) (const nil))
|
||||||
:safe #'stringp)
|
:safe #'stringp)
|
||||||
(make-variable-buffer-local 'clang-format-style)
|
(make-variable-buffer-local 'clang-format-style)
|
||||||
|
|
||||||
|
(defcustom clang-format-fallback-style "none"
|
||||||
|
"Fallback style to pass to clang-format.
|
||||||
|
|
||||||
|
This style will be used if clang-format-style is set to \"file\"
|
||||||
|
and no .clang-format is found in the directory of the buffer or
|
||||||
|
one of parent directories. Set to \"none\" to disable formatting
|
||||||
|
in such buffers."
|
||||||
|
:group 'clang-format
|
||||||
|
:type 'string
|
||||||
|
:safe #'stringp)
|
||||||
|
(make-variable-buffer-local 'clang-format-fallback-style)
|
||||||
|
|
||||||
(defun clang-format--extract (xml-node)
|
(defun clang-format--extract (xml-node)
|
||||||
"Extract replacements and cursor information from XML-NODE."
|
"Extract replacements and cursor information from XML-NODE."
|
||||||
(unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
|
(unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
|
||||||
|
@ -69,7 +83,7 @@ of the buffer."
|
||||||
(let* ((children (xml-node-children node))
|
(let* ((children (xml-node-children node))
|
||||||
(text (car children)))
|
(text (car children)))
|
||||||
(cl-case (xml-node-name node)
|
(cl-case (xml-node-name node)
|
||||||
('replacement
|
(replacement
|
||||||
(let* ((offset (xml-get-attribute-or-nil node 'offset))
|
(let* ((offset (xml-get-attribute-or-nil node 'offset))
|
||||||
(length (xml-get-attribute-or-nil node 'length)))
|
(length (xml-get-attribute-or-nil node 'length)))
|
||||||
(when (or (null offset) (null length))
|
(when (or (null offset) (null length))
|
||||||
|
@ -80,7 +94,7 @@ of the buffer."
|
||||||
(setq offset (string-to-number offset))
|
(setq offset (string-to-number offset))
|
||||||
(setq length (string-to-number length))
|
(setq length (string-to-number length))
|
||||||
(push (list offset length text) replacements)))
|
(push (list offset length text) replacements)))
|
||||||
('cursor
|
(cursor
|
||||||
(setq cursor (string-to-number text)))))))
|
(setq cursor (string-to-number text)))))))
|
||||||
|
|
||||||
;; Sort by decreasing offset, length.
|
;; Sort by decreasing offset, length.
|
||||||
|
@ -119,10 +133,12 @@ is a zero-based file offset, assuming ‘utf-8-unix’ coding."
|
||||||
(byte-to-position (1+ byte)))))
|
(byte-to-position (1+ byte)))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun clang-format-region (start end &optional style)
|
(defun clang-format-region (start end &optional style assume-file-name)
|
||||||
"Use clang-format to format the code between START and END according to STYLE.
|
"Use clang-format to format the code between START and END according to STYLE.
|
||||||
If called interactively uses the region or the current statement if there
|
If called interactively uses the region or the current statement if there is no
|
||||||
is no active region. If no style is given uses `clang-format-style'."
|
no active region. If no STYLE is given uses `clang-format-style'. Use
|
||||||
|
ASSUME-FILE-NAME to locate a style config file, if no ASSUME-FILE-NAME is given
|
||||||
|
uses the function `buffer-file-name'."
|
||||||
(interactive
|
(interactive
|
||||||
(if (use-region-p)
|
(if (use-region-p)
|
||||||
(list (region-beginning) (region-end))
|
(list (region-beginning) (region-end))
|
||||||
|
@ -131,6 +147,9 @@ is no active region. If no style is given uses `clang-format-style'."
|
||||||
(unless style
|
(unless style
|
||||||
(setq style clang-format-style))
|
(setq style clang-format-style))
|
||||||
|
|
||||||
|
(unless assume-file-name
|
||||||
|
(setq assume-file-name (buffer-file-name (buffer-base-buffer))))
|
||||||
|
|
||||||
(let ((file-start (clang-format--bufferpos-to-filepos start 'approximate
|
(let ((file-start (clang-format--bufferpos-to-filepos start 'approximate
|
||||||
'utf-8-unix))
|
'utf-8-unix))
|
||||||
(file-end (clang-format--bufferpos-to-filepos end 'approximate
|
(file-end (clang-format--bufferpos-to-filepos end 'approximate
|
||||||
|
@ -144,16 +163,22 @@ is no active region. If no style is given uses `clang-format-style'."
|
||||||
;; always use ‘utf-8-unix’ and ignore the buffer coding system.
|
;; always use ‘utf-8-unix’ and ignore the buffer coding system.
|
||||||
(default-process-coding-system '(utf-8-unix . utf-8-unix)))
|
(default-process-coding-system '(utf-8-unix . utf-8-unix)))
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(let ((status (call-process-region
|
(let ((status (apply #'call-process-region
|
||||||
nil nil clang-format-executable
|
nil nil clang-format-executable
|
||||||
nil `(,temp-buffer ,temp-file) nil
|
nil `(,temp-buffer ,temp-file) nil
|
||||||
|
`("-output-replacements-xml"
|
||||||
"-output-replacements-xml"
|
;; Guard against a nil assume-file-name.
|
||||||
"-assume-filename" (or (buffer-file-name) "")
|
;; If the clang-format option -assume-filename
|
||||||
"-style" style
|
;; is given a blank string it will crash as per
|
||||||
"-offset" (number-to-string file-start)
|
;; the following bug report
|
||||||
"-length" (number-to-string (- file-end file-start))
|
;; https://bugs.llvm.org/show_bug.cgi?id=34667
|
||||||
"-cursor" (number-to-string cursor)))
|
,@(and assume-file-name
|
||||||
|
(list "-assume-filename" assume-file-name))
|
||||||
|
,@(and style (list "-style" style))
|
||||||
|
"-fallback-style" ,clang-format-fallback-style
|
||||||
|
"-offset" ,(number-to-string file-start)
|
||||||
|
"-length" ,(number-to-string (- file-end file-start))
|
||||||
|
"-cursor" ,(number-to-string cursor))))
|
||||||
(stderr (with-temp-buffer
|
(stderr (with-temp-buffer
|
||||||
(unless (zerop (cadr (insert-file-contents temp-file)))
|
(unless (zerop (cadr (insert-file-contents temp-file)))
|
||||||
(insert ": "))
|
(insert ": "))
|
||||||
|
@ -181,10 +206,13 @@ is no active region. If no style is given uses `clang-format-style'."
|
||||||
(when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
|
(when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun clang-format-buffer (&optional style)
|
(defun clang-format-buffer (&optional style assume-file-name)
|
||||||
"Use clang-format to format the current buffer according to STYLE."
|
"Use clang-format to format the current buffer according to STYLE.
|
||||||
|
If no STYLE is given uses `clang-format-style'. Use ASSUME-FILE-NAME
|
||||||
|
to locate a style config file. If no ASSUME-FILE-NAME is given uses
|
||||||
|
the function `buffer-file-name'."
|
||||||
(interactive)
|
(interactive)
|
||||||
(clang-format-region (point-min) (point-max) style))
|
(clang-format-region (point-min) (point-max) style assume-file-name))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defalias 'clang-format 'clang-format-region)
|
(defalias 'clang-format 'clang-format-region)
|
||||||
|
|
|
@ -2,11 +2,19 @@
|
||||||
# - Change 'binary' if clang-format is not on the path (see below).
|
# - Change 'binary' if clang-format is not on the path (see below).
|
||||||
# - Add to your .vimrc:
|
# - Add to your .vimrc:
|
||||||
#
|
#
|
||||||
|
# if has('python')
|
||||||
# map <C-I> :pyf <path-to-this-file>/clang-format.py<cr>
|
# map <C-I> :pyf <path-to-this-file>/clang-format.py<cr>
|
||||||
# imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
|
# imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
|
||||||
|
# elseif has('python3')
|
||||||
|
# map <C-I> :py3f <path-to-this-file>/clang-format.py<cr>
|
||||||
|
# imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr>
|
||||||
|
# endif
|
||||||
#
|
#
|
||||||
# The first line enables clang-format for NORMAL and VISUAL mode, the second
|
# The if-elseif-endif conditional should pick either the python3 or python2
|
||||||
# line adds support for INSERT mode. Change "C-I" to another binding if you
|
# integration depending on your vim setup.
|
||||||
|
#
|
||||||
|
# The first mapping enables clang-format for NORMAL and VISUAL mode, the second
|
||||||
|
# mapping adds support for INSERT mode. Change "C-I" to another binding if you
|
||||||
# need clang-format on a different key (C-I stands for Ctrl+i).
|
# need clang-format on a different key (C-I stands for Ctrl+i).
|
||||||
#
|
#
|
||||||
# With this integration you can press the bound key and clang-format will
|
# With this integration you can press the bound key and clang-format will
|
||||||
|
@ -20,15 +28,20 @@
|
||||||
# like:
|
# like:
|
||||||
# :function FormatFile()
|
# :function FormatFile()
|
||||||
# : let l:lines="all"
|
# : let l:lines="all"
|
||||||
|
# : if has('python')
|
||||||
# : pyf <path-to-this-file>/clang-format.py
|
# : pyf <path-to-this-file>/clang-format.py
|
||||||
|
# : elseif has('python3')
|
||||||
|
# : py3f <path-to-this-file>/clang-format.py
|
||||||
|
# : endif
|
||||||
# :endfunction
|
# :endfunction
|
||||||
#
|
#
|
||||||
# It operates on the current, potentially unsaved buffer and does not create
|
# It operates on the current, potentially unsaved buffer and does not create
|
||||||
# or save any files. To revert a formatting, just undo.
|
# or save any files. To revert a formatting, just undo.
|
||||||
from __future__ import print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import difflib
|
import difflib
|
||||||
import json
|
import json
|
||||||
|
import os.path
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
@ -36,72 +49,90 @@ import vim
|
||||||
|
|
||||||
# set g:clang_format_path to the path to clang-format if it is not on the path
|
# set g:clang_format_path to the path to clang-format if it is not on the path
|
||||||
# Change this to the full path if clang-format is not on the path.
|
# Change this to the full path if clang-format is not on the path.
|
||||||
binary = 'clang-format'
|
binary = "clang-format"
|
||||||
if vim.eval('exists("g:clang_format_path")') == "1":
|
if vim.eval('exists("g:clang_format_path")') == "1":
|
||||||
binary = vim.eval('g:clang_format_path')
|
binary = vim.eval("g:clang_format_path")
|
||||||
|
|
||||||
# Change this to format according to other formatting styles. See the output of
|
# Change this to format according to other formatting styles. See the output of
|
||||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||||
# used.
|
# used.
|
||||||
style = 'file'
|
style = None
|
||||||
fallback_style = None
|
fallback_style = None
|
||||||
if vim.eval('exists("g:clang_format_fallback_style")') == "1":
|
if vim.eval('exists("g:clang_format_fallback_style")') == "1":
|
||||||
fallback_style = vim.eval('g:clang_format_fallback_style')
|
fallback_style = vim.eval("g:clang_format_fallback_style")
|
||||||
|
|
||||||
|
|
||||||
def get_buffer(encoding):
|
def get_buffer(encoding):
|
||||||
if platform.python_version_tuple()[0] == '3':
|
if platform.python_version_tuple()[0] == "3":
|
||||||
return vim.current.buffer
|
return vim.current.buffer
|
||||||
return [line.decode(encoding) for line in vim.current.buffer]
|
return [line.decode(encoding) for line in vim.current.buffer]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Get the current text.
|
# Get the current text.
|
||||||
encoding = vim.eval("&encoding")
|
encoding = vim.eval("&encoding")
|
||||||
buf = get_buffer(encoding)
|
buf = get_buffer(encoding)
|
||||||
text = '\n'.join(buf)
|
# Join the buffer into a single string with a terminating newline
|
||||||
|
text = ("\n".join(buf) + "\n").encode(encoding)
|
||||||
|
|
||||||
# Determine range to format.
|
# Determine range to format.
|
||||||
if vim.eval('exists("l:lines")') == '1':
|
if vim.eval('exists("l:lines")') == "1":
|
||||||
lines = vim.eval('l:lines')
|
lines = ["-lines", vim.eval("l:lines")]
|
||||||
elif vim.eval('exists("l:formatdiff")') == '1':
|
elif vim.eval('exists("l:formatdiff")') == "1" and os.path.exists(
|
||||||
with open(vim.current.buffer.name, 'r') as f:
|
vim.current.buffer.name
|
||||||
ondisk = f.read().splitlines();
|
):
|
||||||
|
with open(vim.current.buffer.name, "r") as f:
|
||||||
|
ondisk = f.read().splitlines()
|
||||||
sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
|
sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
|
||||||
lines = []
|
lines = []
|
||||||
for op in reversed(sequence.get_opcodes()):
|
for op in reversed(sequence.get_opcodes()):
|
||||||
if op[0] not in ['equal', 'delete']:
|
if op[0] not in ["equal", "delete"]:
|
||||||
lines += ['-lines', '%s:%s' % (op[3] + 1, op[4])]
|
lines += ["-lines", "%s:%s" % (op[3] + 1, op[4])]
|
||||||
if lines == []:
|
if lines == []:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
lines = ['-lines', '%s:%s' % (vim.current.range.start + 1,
|
lines = [
|
||||||
vim.current.range.end + 1)]
|
"-lines",
|
||||||
|
"%s:%s" % (vim.current.range.start + 1, vim.current.range.end + 1),
|
||||||
|
]
|
||||||
|
|
||||||
# Determine the cursor position.
|
# Convert cursor (line, col) to bytes.
|
||||||
cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2
|
# Don't use line2byte: https://github.com/vim/vim/issues/5930
|
||||||
if cursor < 0:
|
_, cursor_line, cursor_col, _ = vim.eval('getpos(".")') # 1-based
|
||||||
print('Couldn\'t determine cursor position. Is your file empty?')
|
cursor_byte = 0
|
||||||
|
for line in text.split(b"\n")[: int(cursor_line) - 1]:
|
||||||
|
cursor_byte += len(line) + 1
|
||||||
|
cursor_byte += int(cursor_col) - 1
|
||||||
|
if cursor_byte < 0:
|
||||||
|
print("Couldn't determine cursor position. Is your file empty?")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format.
|
# Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format.
|
||||||
startupinfo = None
|
startupinfo = None
|
||||||
if sys.platform.startswith('win32'):
|
if sys.platform.startswith("win32"):
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||||
|
|
||||||
# Call formatter.
|
# Call formatter.
|
||||||
command = [binary, '-style', style, '-cursor', str(cursor)]
|
command = [binary, "-cursor", str(cursor_byte)]
|
||||||
if lines != 'all':
|
if lines != ["-lines", "all"]:
|
||||||
command += lines
|
command += lines
|
||||||
|
if style:
|
||||||
|
command.extend(["-style", style])
|
||||||
if fallback_style:
|
if fallback_style:
|
||||||
command.extend(['-fallback-style', fallback_style])
|
command.extend(["-fallback-style", fallback_style])
|
||||||
if vim.current.buffer.name:
|
if vim.current.buffer.name:
|
||||||
command.extend(['-assume-filename', vim.current.buffer.name])
|
command.extend(["-assume-filename", vim.current.buffer.name])
|
||||||
p = subprocess.Popen(command,
|
p = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
command,
|
||||||
stdin=subprocess.PIPE, startupinfo=startupinfo)
|
stdout=subprocess.PIPE,
|
||||||
stdout, stderr = p.communicate(input=text.encode(encoding))
|
stderr=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
startupinfo=startupinfo,
|
||||||
|
)
|
||||||
|
stdout, stderr = p.communicate(input=text)
|
||||||
|
|
||||||
# If successful, replace buffer contents.
|
# If successful, replace buffer contents.
|
||||||
if stderr:
|
if stderr:
|
||||||
|
@ -109,19 +140,29 @@ def main():
|
||||||
|
|
||||||
if not stdout:
|
if not stdout:
|
||||||
print(
|
print(
|
||||||
'No output from clang-format (crashed?).\n'
|
"No output from clang-format (crashed?).\n"
|
||||||
'Please report to bugs.llvm.org.'
|
"Please report to bugs.llvm.org."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
lines = stdout.decode(encoding).split('\n')
|
header, content = stdout.split(b"\n", 1)
|
||||||
output = json.loads(lines[0])
|
header = json.loads(header.decode("utf-8"))
|
||||||
lines = lines[1:]
|
# Strip off the trailing newline (added above).
|
||||||
|
# This maintains trailing empty lines present in the buffer if
|
||||||
|
# the -lines specification requests them to remain unchanged.
|
||||||
|
lines = content.decode(encoding).split("\n")[:-1]
|
||||||
sequence = difflib.SequenceMatcher(None, buf, lines)
|
sequence = difflib.SequenceMatcher(None, buf, lines)
|
||||||
for op in reversed(sequence.get_opcodes()):
|
for op in reversed(sequence.get_opcodes()):
|
||||||
if op[0] is not 'equal':
|
if op[0] != "equal":
|
||||||
vim.current.buffer[op[1] : op[2]] = lines[op[3] : op[4]]
|
vim.current.buffer[op[1] : op[2]] = lines[op[3] : op[4]]
|
||||||
if output.get('IncompleteFormat'):
|
if header.get("IncompleteFormat"):
|
||||||
print('clang-format: incomplete (syntax errors)')
|
print("clang-format: incomplete (syntax errors)")
|
||||||
vim.command('goto %d' % (output['Cursor'] + 1))
|
# Convert cursor bytes to (line, col)
|
||||||
|
# Don't use goto: https://github.com/vim/vim/issues/5930
|
||||||
|
cursor_byte = int(header["Cursor"])
|
||||||
|
prefix = content[0:cursor_byte]
|
||||||
|
cursor_line = 1 + prefix.count(b"\n")
|
||||||
|
cursor_column = 1 + len(prefix.rsplit(b"\n", 1)[-1])
|
||||||
|
vim.command("call cursor(%d, %d)" % (cursor_line, cursor_column))
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
#
|
#
|
||||||
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
||||||
#
|
#
|
||||||
# The LLVM Compiler Infrastructure
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
#
|
# See https://llvm.org/LICENSE.txt for license information.
|
||||||
# This file is distributed under the University of Illinois Open Source
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
# License. See LICENSE.TXT for details.
|
|
||||||
#
|
#
|
||||||
#===------------------------------------------------------------------------===#
|
#===------------------------------------------------------------------------===#
|
||||||
|
|
||||||
|
@ -23,7 +22,7 @@ git clang-format -h
|
||||||
Requires Python 2.7 or Python 3
|
Requires Python 2.7 or Python 3
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
import argparse
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
|
@ -33,12 +32,23 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]'
|
usage = ('git clang-format [OPTIONS] [<commit>] [<commit>|--staged] '
|
||||||
|
'[--] [<file>...]')
|
||||||
|
|
||||||
desc = '''
|
desc = '''
|
||||||
If zero or one commits are given, run clang-format on all lines that differ
|
If zero or one commits are given, run clang-format on all lines that differ
|
||||||
between the working directory and <commit>, which defaults to HEAD. Changes are
|
between the working directory and <commit>, which defaults to HEAD. Changes are
|
||||||
only applied to the working directory.
|
only applied to the working directory, or in the stage/index.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
To format staged changes, i.e everything that's been `git add`ed:
|
||||||
|
git clang-format
|
||||||
|
|
||||||
|
To also format everything touched in the most recent commit:
|
||||||
|
git clang-format HEAD~1
|
||||||
|
|
||||||
|
If you're on a branch off main, to format everything touched on your branch:
|
||||||
|
git clang-format main
|
||||||
|
|
||||||
If two commits are given (requires --diff), run clang-format on all lines in the
|
If two commits are given (requires --diff), run clang-format on all lines in the
|
||||||
second <commit> that differ from the first <commit>.
|
second <commit> that differ from the first <commit>.
|
||||||
|
@ -46,7 +56,7 @@ second <commit> that differ from the first <commit>.
|
||||||
The following git-config settings set the default of the corresponding option:
|
The following git-config settings set the default of the corresponding option:
|
||||||
clangFormat.binary
|
clangFormat.binary
|
||||||
clangFormat.commit
|
clangFormat.commit
|
||||||
clangFormat.extension
|
clangFormat.extensions
|
||||||
clangFormat.style
|
clangFormat.style
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -78,12 +88,17 @@ def main():
|
||||||
'c', 'h', # C
|
'c', 'h', # C
|
||||||
'm', # ObjC
|
'm', # ObjC
|
||||||
'mm', # ObjC++
|
'mm', # ObjC++
|
||||||
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++
|
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', 'inc', # C++
|
||||||
|
'ccm', 'cppm', 'cxxm', 'c++m', # C++ Modules
|
||||||
|
'cu', 'cuh', # CUDA
|
||||||
# Other languages that clang-format supports
|
# Other languages that clang-format supports
|
||||||
'proto', 'protodevel', # Protocol Buffers
|
'proto', 'protodevel', # Protocol Buffers
|
||||||
'java', # Java
|
'java', # Java
|
||||||
'js', # JavaScript
|
'js', # JavaScript
|
||||||
'ts', # TypeScript
|
'ts', # TypeScript
|
||||||
|
'cs', # C Sharp
|
||||||
|
'json', # Json
|
||||||
|
'sv', 'svh', 'v', 'vh', # Verilog
|
||||||
])
|
])
|
||||||
|
|
||||||
p = argparse.ArgumentParser(
|
p = argparse.ArgumentParser(
|
||||||
|
@ -97,6 +112,8 @@ def main():
|
||||||
help='default commit to use if none is specified'),
|
help='default commit to use if none is specified'),
|
||||||
p.add_argument('--diff', action='store_true',
|
p.add_argument('--diff', action='store_true',
|
||||||
help='print a diff instead of applying the changes')
|
help='print a diff instead of applying the changes')
|
||||||
|
p.add_argument('--diffstat', action='store_true',
|
||||||
|
help='print a diffstat instead of applying the changes')
|
||||||
p.add_argument('--extensions',
|
p.add_argument('--extensions',
|
||||||
default=config.get('clangformat.extensions',
|
default=config.get('clangformat.extensions',
|
||||||
default_extensions),
|
default_extensions),
|
||||||
|
@ -108,11 +125,17 @@ def main():
|
||||||
help='select hunks interactively')
|
help='select hunks interactively')
|
||||||
p.add_argument('-q', '--quiet', action='count', default=0,
|
p.add_argument('-q', '--quiet', action='count', default=0,
|
||||||
help='print less information')
|
help='print less information')
|
||||||
|
p.add_argument('--staged', '--cached', action='store_true',
|
||||||
|
help='format lines in the stage instead of the working dir')
|
||||||
p.add_argument('--style',
|
p.add_argument('--style',
|
||||||
default=config.get('clangformat.style', None),
|
default=config.get('clangformat.style', None),
|
||||||
help='passed to clang-format'),
|
help='passed to clang-format'),
|
||||||
p.add_argument('-v', '--verbose', action='count', default=0,
|
p.add_argument('-v', '--verbose', action='count', default=0,
|
||||||
help='print extra information')
|
help='print extra information')
|
||||||
|
p.add_argument('--diff_from_common_commit', action='store_true',
|
||||||
|
help=('diff from the last common commit for commits in '
|
||||||
|
'separate branches rather than the exact point of the '
|
||||||
|
'commits'))
|
||||||
# We gather all the remaining positional arguments into 'args' since we need
|
# We gather all the remaining positional arguments into 'args' since we need
|
||||||
# to use some heuristics to determine whether or not <commit> was present.
|
# to use some heuristics to determine whether or not <commit> was present.
|
||||||
# However, to print pretty messages, we make use of metavar and help.
|
# However, to print pretty messages, we make use of metavar and help.
|
||||||
|
@ -126,52 +149,74 @@ def main():
|
||||||
del opts.quiet
|
del opts.quiet
|
||||||
|
|
||||||
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
|
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||||
if len(commits) > 1:
|
|
||||||
if not opts.diff:
|
|
||||||
die('--diff is required when two commits are given')
|
|
||||||
else:
|
|
||||||
if len(commits) > 2:
|
if len(commits) > 2:
|
||||||
die('at most two commits allowed; %d given' % len(commits))
|
die('at most two commits allowed; %d given' % len(commits))
|
||||||
changed_lines = compute_diff_and_extract_lines(commits, files)
|
if len(commits) == 2:
|
||||||
|
if opts.staged:
|
||||||
|
die('--staged is not allowed when two commits are given')
|
||||||
|
if not opts.diff:
|
||||||
|
die('--diff is required when two commits are given')
|
||||||
|
elif opts.diff_from_common_commit:
|
||||||
|
die('--diff_from_common_commit is only allowed when two commits are given')
|
||||||
|
|
||||||
|
if os.path.dirname(opts.binary):
|
||||||
|
opts.binary = os.path.abspath(opts.binary)
|
||||||
|
|
||||||
|
changed_lines = compute_diff_and_extract_lines(commits,
|
||||||
|
files,
|
||||||
|
opts.staged,
|
||||||
|
opts.diff_from_common_commit)
|
||||||
if opts.verbose >= 1:
|
if opts.verbose >= 1:
|
||||||
ignored_files = set(changed_lines)
|
ignored_files = set(changed_lines)
|
||||||
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
||||||
|
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||||
|
# those files.
|
||||||
|
cd_to_toplevel()
|
||||||
|
filter_symlinks(changed_lines)
|
||||||
if opts.verbose >= 1:
|
if opts.verbose >= 1:
|
||||||
ignored_files.difference_update(changed_lines)
|
ignored_files.difference_update(changed_lines)
|
||||||
if ignored_files:
|
if ignored_files:
|
||||||
print('Ignoring changes in the following files (wrong extension):')
|
print(
|
||||||
|
'Ignoring changes in the following files (wrong extension or symlink):')
|
||||||
for filename in ignored_files:
|
for filename in ignored_files:
|
||||||
print(' %s' % filename)
|
print(' %s' % filename)
|
||||||
if changed_lines:
|
if changed_lines:
|
||||||
print('Running clang-format on the following files:')
|
print('Running clang-format on the following files:')
|
||||||
for filename in changed_lines:
|
for filename in changed_lines:
|
||||||
print(' %s' % filename)
|
print(' %s' % filename)
|
||||||
|
|
||||||
if not changed_lines:
|
if not changed_lines:
|
||||||
|
if opts.verbose >= 0:
|
||||||
print('no modified files to format')
|
print('no modified files to format')
|
||||||
return
|
return 0
|
||||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
|
||||||
# those files.
|
|
||||||
cd_to_toplevel()
|
|
||||||
if len(commits) > 1:
|
if len(commits) > 1:
|
||||||
old_tree = commits[1]
|
old_tree = commits[1]
|
||||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
revision = old_tree
|
||||||
revision=commits[1],
|
elif opts.staged:
|
||||||
binary=opts.binary,
|
old_tree = create_tree_from_index(changed_lines)
|
||||||
style=opts.style)
|
revision = ''
|
||||||
else:
|
else:
|
||||||
old_tree = create_tree_from_workdir(changed_lines)
|
old_tree = create_tree_from_workdir(changed_lines)
|
||||||
|
revision = None
|
||||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||||
|
revision,
|
||||||
binary=opts.binary,
|
binary=opts.binary,
|
||||||
style=opts.style)
|
style=opts.style)
|
||||||
if opts.verbose >= 1:
|
if opts.verbose >= 1:
|
||||||
print('old tree: %s' % old_tree)
|
print('old tree: %s' % old_tree)
|
||||||
print('new tree: %s' % new_tree)
|
print('new tree: %s' % new_tree)
|
||||||
|
|
||||||
if old_tree == new_tree:
|
if old_tree == new_tree:
|
||||||
if opts.verbose >= 0:
|
if opts.verbose >= 0:
|
||||||
print('clang-format did not modify any files')
|
print('clang-format did not modify any files')
|
||||||
elif opts.diff:
|
return 0
|
||||||
print_diff(old_tree, new_tree)
|
|
||||||
else:
|
if opts.diff:
|
||||||
|
return print_diff(old_tree, new_tree)
|
||||||
|
if opts.diffstat:
|
||||||
|
return print_diffstat(old_tree, new_tree)
|
||||||
|
|
||||||
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
||||||
patch_mode=opts.patch)
|
patch_mode=opts.patch)
|
||||||
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||||
|
@ -179,6 +224,8 @@ def main():
|
||||||
for filename in changed_files:
|
for filename in changed_files:
|
||||||
print(' %s' % filename)
|
print(' %s' % filename)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def load_git_config(non_string_options=None):
|
def load_git_config(non_string_options=None):
|
||||||
"""Return the git configuration as a dictionary.
|
"""Return the git configuration as a dictionary.
|
||||||
|
@ -191,7 +238,12 @@ def load_git_config(non_string_options=None):
|
||||||
out = {}
|
out = {}
|
||||||
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
||||||
if entry:
|
if entry:
|
||||||
|
if '\n' in entry:
|
||||||
name, value = entry.split('\n', 1)
|
name, value = entry.split('\n', 1)
|
||||||
|
else:
|
||||||
|
# A setting with no '=' ('\n' with --null) is implicitly 'true'
|
||||||
|
name = entry
|
||||||
|
value = 'true'
|
||||||
if name in non_string_options:
|
if name in non_string_options:
|
||||||
value = run('git', 'config', non_string_options[name], name)
|
value = run('git', 'config', non_string_options[name], name)
|
||||||
out[name] = value
|
out[name] = value
|
||||||
|
@ -261,9 +313,9 @@ def get_object_type(value):
|
||||||
return convert_string(stdout.strip())
|
return convert_string(stdout.strip())
|
||||||
|
|
||||||
|
|
||||||
def compute_diff_and_extract_lines(commits, files):
|
def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit):
|
||||||
"""Calls compute_diff() followed by extract_lines()."""
|
"""Calls compute_diff() followed by extract_lines()."""
|
||||||
diff_process = compute_diff(commits, files)
|
diff_process = compute_diff(commits, files, staged, diff_common_commit)
|
||||||
changed_lines = extract_lines(diff_process.stdout)
|
changed_lines = extract_lines(diff_process.stdout)
|
||||||
diff_process.stdout.close()
|
diff_process.stdout.close()
|
||||||
diff_process.wait()
|
diff_process.wait()
|
||||||
|
@ -273,17 +325,24 @@ def compute_diff_and_extract_lines(commits, files):
|
||||||
return changed_lines
|
return changed_lines
|
||||||
|
|
||||||
|
|
||||||
def compute_diff(commits, files):
|
def compute_diff(commits, files, staged, diff_common_commit):
|
||||||
"""Return a subprocess object producing the diff from `commits`.
|
"""Return a subprocess object producing the diff from `commits`.
|
||||||
|
|
||||||
The return value's `stdin` file object will produce a patch with the
|
The return value's `stdin` file object will produce a patch with the
|
||||||
differences between the working directory and the first commit if a single
|
differences between the working directory (or stage if --staged is used) and
|
||||||
one was specified, or the difference between both specified commits, filtered
|
the first commit if a single one was specified, or the difference between
|
||||||
on `files` (if non-empty). Zero context lines are used in the patch."""
|
both specified commits, filtered on `files` (if non-empty).
|
||||||
|
Zero context lines are used in the patch."""
|
||||||
git_tool = 'diff-index'
|
git_tool = 'diff-index'
|
||||||
if len(commits) > 1:
|
extra_args = []
|
||||||
|
if len(commits) == 2:
|
||||||
git_tool = 'diff-tree'
|
git_tool = 'diff-tree'
|
||||||
cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--']
|
if diff_common_commit:
|
||||||
|
commits = [f'{commits[0]}...{commits[1]}']
|
||||||
|
elif staged:
|
||||||
|
extra_args += ['--cached']
|
||||||
|
|
||||||
|
cmd = ['git', git_tool, '-p', '-U0'] + extra_args + commits + ['--']
|
||||||
cmd.extend(files)
|
cmd.extend(files)
|
||||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
p.stdin.close()
|
p.stdin.close()
|
||||||
|
@ -304,14 +363,17 @@ def extract_lines(patch_file):
|
||||||
line = convert_string(line)
|
line = convert_string(line)
|
||||||
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
||||||
if match:
|
if match:
|
||||||
filename = match.group(1).rstrip('\r\n')
|
filename = match.group(1).rstrip('\r\n\t')
|
||||||
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
||||||
if match:
|
if match:
|
||||||
start_line = int(match.group(1))
|
start_line = int(match.group(1))
|
||||||
line_count = 1
|
line_count = 1
|
||||||
if match.group(3):
|
if match.group(3):
|
||||||
line_count = int(match.group(3))
|
line_count = int(match.group(3))
|
||||||
if line_count > 0:
|
if line_count == 0:
|
||||||
|
line_count = 1
|
||||||
|
if start_line == 0:
|
||||||
|
continue
|
||||||
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
@ -330,6 +392,13 @@ def filter_by_extension(dictionary, allowed_extensions):
|
||||||
del dictionary[filename]
|
del dictionary[filename]
|
||||||
|
|
||||||
|
|
||||||
|
def filter_symlinks(dictionary):
|
||||||
|
"""Delete every key in `dictionary` that is a symlink."""
|
||||||
|
for filename in list(dictionary.keys()):
|
||||||
|
if os.path.islink(filename):
|
||||||
|
del dictionary[filename]
|
||||||
|
|
||||||
|
|
||||||
def cd_to_toplevel():
|
def cd_to_toplevel():
|
||||||
"""Change to the top level of the git repository."""
|
"""Change to the top level of the git repository."""
|
||||||
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
||||||
|
@ -343,11 +412,29 @@ def create_tree_from_workdir(filenames):
|
||||||
return create_tree(filenames, '--stdin')
|
return create_tree(filenames, '--stdin')
|
||||||
|
|
||||||
|
|
||||||
|
def create_tree_from_index(filenames):
|
||||||
|
# Copy the environment, because the files have to be read from the original
|
||||||
|
# index.
|
||||||
|
env = os.environ.copy()
|
||||||
|
def index_contents_generator():
|
||||||
|
for filename in filenames:
|
||||||
|
git_ls_files_cmd = ['git', 'ls-files', '--stage', '-z', '--', filename]
|
||||||
|
git_ls_files = subprocess.Popen(git_ls_files_cmd, env=env,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
stdout = git_ls_files.communicate()[0]
|
||||||
|
yield convert_string(stdout.split(b'\0')[0])
|
||||||
|
return create_tree(index_contents_generator(), '--index-info')
|
||||||
|
|
||||||
|
|
||||||
def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
||||||
binary='clang-format', style=None):
|
binary='clang-format', style=None):
|
||||||
"""Run clang-format on each file and save the result to a git tree.
|
"""Run clang-format on each file and save the result to a git tree.
|
||||||
|
|
||||||
Returns the object ID (SHA-1) of the created tree."""
|
Returns the object ID (SHA-1) of the created tree."""
|
||||||
|
# Copy the environment when formatting the files in the index, because the
|
||||||
|
# files have to be read from the original index.
|
||||||
|
env = os.environ.copy() if revision == '' else None
|
||||||
def iteritems(container):
|
def iteritems(container):
|
||||||
try:
|
try:
|
||||||
return container.iteritems() # Python 2
|
return container.iteritems() # Python 2
|
||||||
|
@ -355,11 +442,15 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
||||||
return container.items() # Python 3
|
return container.items() # Python 3
|
||||||
def index_info_generator():
|
def index_info_generator():
|
||||||
for filename, line_ranges in iteritems(changed_lines):
|
for filename, line_ranges in iteritems(changed_lines):
|
||||||
if revision:
|
if revision is not None:
|
||||||
|
if len(revision) > 0:
|
||||||
git_metadata_cmd = ['git', 'ls-tree',
|
git_metadata_cmd = ['git', 'ls-tree',
|
||||||
'%s:%s' % (revision, os.path.dirname(filename)),
|
'%s:%s' % (revision, os.path.dirname(filename)),
|
||||||
os.path.basename(filename)]
|
os.path.basename(filename)]
|
||||||
git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE,
|
else:
|
||||||
|
git_metadata_cmd = ['git', 'ls-files', '--stage', '--', filename]
|
||||||
|
git_metadata = subprocess.Popen(git_metadata_cmd, env=env,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
stdout = git_metadata.communicate()[0]
|
stdout = git_metadata.communicate()[0]
|
||||||
mode = oct(int(stdout.split()[0], 8))
|
mode = oct(int(stdout.split()[0], 8))
|
||||||
|
@ -371,7 +462,8 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
||||||
blob_id = clang_format_to_blob(filename, line_ranges,
|
blob_id = clang_format_to_blob(filename, line_ranges,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
binary=binary,
|
binary=binary,
|
||||||
style=style)
|
style=style,
|
||||||
|
env=env)
|
||||||
yield '%s %s\t%s' % (mode, blob_id, filename)
|
yield '%s %s\t%s' % (mode, blob_id, filename)
|
||||||
return create_tree(index_info_generator(), '--index-info')
|
return create_tree(index_info_generator(), '--index-info')
|
||||||
|
|
||||||
|
@ -397,11 +489,12 @@ def create_tree(input_lines, mode):
|
||||||
|
|
||||||
|
|
||||||
def clang_format_to_blob(filename, line_ranges, revision=None,
|
def clang_format_to_blob(filename, line_ranges, revision=None,
|
||||||
binary='clang-format', style=None):
|
binary='clang-format', style=None, env=None):
|
||||||
"""Run clang-format on the given file and save the result to a git blob.
|
"""Run clang-format on the given file and save the result to a git blob.
|
||||||
|
|
||||||
Runs on the file in `revision` if not None, or on the file in the working
|
Runs on the file in `revision` if not None, or on the file in the working
|
||||||
directory if `revision` is None.
|
directory if `revision` is None. Revision can be set to an empty string to run
|
||||||
|
clang-format on the file in the index.
|
||||||
|
|
||||||
Returns the object ID (SHA-1) of the created blob."""
|
Returns the object ID (SHA-1) of the created blob."""
|
||||||
clang_format_cmd = [binary]
|
clang_format_cmd = [binary]
|
||||||
|
@ -410,10 +503,10 @@ def clang_format_to_blob(filename, line_ranges, revision=None,
|
||||||
clang_format_cmd.extend([
|
clang_format_cmd.extend([
|
||||||
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
||||||
for start_line, line_count in line_ranges])
|
for start_line, line_count in line_ranges])
|
||||||
if revision:
|
if revision is not None:
|
||||||
clang_format_cmd.extend(['-assume-filename='+filename])
|
clang_format_cmd.extend(['-assume-filename='+filename])
|
||||||
git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
|
git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
|
||||||
git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE,
|
git_show = subprocess.Popen(git_show_cmd, env=env, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
git_show.stdin.close()
|
git_show.stdin.close()
|
||||||
clang_format_stdin = git_show.stdout
|
clang_format_stdin = git_show.stdout
|
||||||
|
@ -485,9 +578,20 @@ def print_diff(old_tree, new_tree):
|
||||||
# We also only print modified files since `new_tree` only contains the files
|
# We also only print modified files since `new_tree` only contains the files
|
||||||
# that were modified, so unmodified files would show as deleted without the
|
# that were modified, so unmodified files would show as deleted without the
|
||||||
# filter.
|
# filter.
|
||||||
subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree,
|
return subprocess.run(['git', 'diff', '--diff-filter=M',
|
||||||
'--'])
|
'--exit-code', old_tree, new_tree]).returncode
|
||||||
|
|
||||||
|
def print_diffstat(old_tree, new_tree):
|
||||||
|
"""Print the diffstat between the two trees to stdout."""
|
||||||
|
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
|
||||||
|
# is expected to be viewed by the user, and only the former does nice things
|
||||||
|
# like color and pagination.
|
||||||
|
#
|
||||||
|
# We also only print modified files since `new_tree` only contains the files
|
||||||
|
# that were modified, so unmodified files would show as deleted without the
|
||||||
|
# filter.
|
||||||
|
return subprocess.run(['git', 'diff', '--diff-filter=M', '--exit-code',
|
||||||
|
'--stat', old_tree, new_tree]).returncode
|
||||||
|
|
||||||
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||||
"""Apply the changes in `new_tree` to the working directory.
|
"""Apply the changes in `new_tree` to the working directory.
|
||||||
|
@ -513,16 +617,16 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||||
# better message, "Apply ... to index and worktree". This is not quite
|
# better message, "Apply ... to index and worktree". This is not quite
|
||||||
# right, since it won't be applied to the user's index, but oh well.
|
# right, since it won't be applied to the user's index, but oh well.
|
||||||
with temporary_index_file(old_tree):
|
with temporary_index_file(old_tree):
|
||||||
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
|
subprocess.run(['git', 'checkout', '--patch', new_tree], check=True)
|
||||||
index_tree = old_tree
|
index_tree = old_tree
|
||||||
else:
|
else:
|
||||||
with temporary_index_file(new_tree):
|
with temporary_index_file(new_tree):
|
||||||
run('git', 'checkout-index', '-a', '-f')
|
run('git', 'checkout-index', '-f', '--', *changed_files)
|
||||||
return changed_files
|
return changed_files
|
||||||
|
|
||||||
|
|
||||||
def run(*args, **kwargs):
|
def run(*args, **kwargs):
|
||||||
stdin = kwargs.pop('stdin', to_bytes(''))
|
stdin = kwargs.pop('stdin', '')
|
||||||
verbose = kwargs.pop('verbose', True)
|
verbose = kwargs.pop('verbose', True)
|
||||||
strip = kwargs.pop('strip', True)
|
strip = kwargs.pop('strip', True)
|
||||||
for name in kwargs:
|
for name in kwargs:
|
||||||
|
@ -576,4 +680,4 @@ def convert_string(bytes_input):
|
||||||
return str(bytes_input)
|
return str(bytes_input)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
sys.exit(main())
|
||||||
|
|
Loading…
Reference in New Issue