Compare commits

..

8 Commits

Author SHA1 Message Date
VolcaEM
8c84a7e7ec Clang-format 2020-06-01 19:42:54 +02:00
VolcaEM
4d10d3113f hid: Stub GetXpadIDs
Allows Minecraft: Nintendo Switch Edition (a.k.a. old Minecraft) to boot and go ingame
2020-06-01 19:38:44 +02:00
bunnei
058ec22787 Merge pull request #3982 from ReinUsesLisp/membar-cts
shader/other: Implement MEMBAR.CTS
2020-05-30 11:51:42 -04:00
Fernando Sahmkow
9d9ffe0f94 Merge pull request #4017 from ogniK5377/xbyak
Add xbyak external
2020-05-29 21:38:34 -04:00
David Marcec
d0bdd26c26 Add xbyak external 2020-05-30 10:55:27 +10:00
bunnei
87b272699f Merge pull request #4007 from ReinUsesLisp/reduce-logs
maxwell_3d: Reduce severity of logs that can be spammed
2020-05-29 17:29:17 -04:00
ReinUsesLisp
9b06e823ee maxwell_3d: Reduce severity of logs that can be spammed
These logs were killing performance on some games when they were
spammed. Reduce them to Debug severity.
2020-05-28 18:23:25 -03:00
ReinUsesLisp
32e6727dae shader/other: Implement MEMBAR.CTS
This silences an assertion we were hitting and uses workgroup memory
barriers when the game requests it.
2020-05-27 00:19:45 -03:00
15 changed files with 404 additions and 61 deletions

3
.gitmodules vendored
View File

@@ -28,3 +28,6 @@
[submodule "libzip"]
path = externals/libzip/libzip
url = https://github.com/nih-at/libzip.git
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git

View File

@@ -75,3 +75,11 @@ if (ENABLE_WEB_SERVICE)
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto)
endif()
if (NOT TARGET xbyak)
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
add_library(xbyak INTERFACE)
target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
endif()

1
externals/xbyak vendored Submodule

Submodule externals/xbyak added at 82b70e6659

View File

@@ -171,10 +171,12 @@ if(ARCHITECTURE_x86_64)
PRIVATE
x64/cpu_detect.cpp
x64/cpu_detect.h
x64/xbyak_abi.h
x64/xbyak_util.h
)
endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)

266
src/common/x64/xbyak_abi.h Normal file
View File

@@ -0,0 +1,266 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <bitset>
#include <initializer_list>
#include <xbyak.h>
#include "common/assert.h"
namespace Common::X64 {
inline int RegToIndex(const Xbyak::Reg& reg) {
using Kind = Xbyak::Reg::Kind;
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
"RegSet only support GPRs and XMM registers.");
ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
}
inline Xbyak::Reg64 IndexToReg64(int reg_index) {
ASSERT(reg_index < 16);
return Xbyak::Reg64(reg_index);
}
inline Xbyak::Xmm IndexToXmm(int reg_index) {
ASSERT(reg_index >= 16 && reg_index < 32);
return Xbyak::Xmm(reg_index - 16);
}
inline Xbyak::Reg IndexToReg(int reg_index) {
if (reg_index < 16) {
return IndexToReg64(reg_index);
} else {
return IndexToXmm(reg_index);
}
}
inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
std::bitset<32> bits;
for (const Xbyak::Reg& reg : regs) {
bits[RegToIndex(reg)] = true;
}
return bits;
}
const std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
const std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
#ifdef _WIN32
// Microsoft x64 ABI
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
});
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rsi,
Xbyak::util::rdi,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
// XMMs
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr size_t ABI_SHADOW_SPACE = 0x20;
#else
// System V x86-64 ABI
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::rdi,
Xbyak::util::rsi,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
});
constexpr size_t ABI_SHADOW_SPACE = 0;
#endif
inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
size_t needed_frame_size, s32* out_subtraction,
s32* out_xmm_offset) {
const auto count = (regs & ABI_ALL_GPRS).count();
rsp_alignment -= count * 8;
size_t subtraction = 0;
const auto xmm_count = (regs & ABI_ALL_XMMS).count();
if (xmm_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xF;
}
subtraction += 0x10 * xmm_count;
size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += ABI_SHADOW_SPACE;
// Final alignment.
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xF;
*out_subtraction = (s32)subtraction;
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
}
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
for (std::size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_GPRS[i]) {
code.push(IndexToReg64(static_cast<int>(i)));
}
}
if (subtraction != 0) {
code.sub(code.rsp, subtraction);
}
for (int i = 0; i < regs.count(); i++) {
if (regs.test(i) & ABI_ALL_GPRS.test(i)) {
code.push(IndexToReg64(i));
}
}
for (std::size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(static_cast<int>(i)));
xmm_offset += 0x10;
}
}
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
for (std::size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(IndexToXmm(static_cast<int>(i)), code.xword[code.rsp + xmm_offset]);
xmm_offset += 0x10;
}
}
if (subtraction != 0) {
code.add(code.rsp, subtraction);
}
// GPRs need to be popped in reverse order
for (int i = 15; i >= 0; i--) {
if (regs[i]) {
code.pop(IndexToReg64(i));
}
}
}
inline size_t ABI_PushRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment,
size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
for (std::size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_GPRS[i]) {
code.push(IndexToReg64(static_cast<int>(i)));
}
}
if (subtraction != 0) {
code.sub(code.rsp, subtraction);
}
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
if (subtraction != 0) {
code.add(code.rsp, subtraction);
}
// GPRs need to be popped in reverse order
for (int i = 15; i >= 0; i--) {
if (regs[i]) {
code.pop(IndexToReg64(i));
}
}
}
} // namespace Common::X64

View File

@@ -0,0 +1,47 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
#include <xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64

View File

@@ -161,7 +161,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{40, nullptr, "AcquireXpadIdEventHandle"},
{41, nullptr, "ReleaseXpadIdEventHandle"},
{51, &Hid::ActivateXpad, "ActivateXpad"},
{55, nullptr, "GetXpadIds"},
{55, &Hid::GetXpadIDs, "GetXpadIds"},
{56, nullptr, "ActivateJoyXpad"},
{58, nullptr, "GetJoyXpadLifoHandle"},
{59, nullptr, "GetJoyXpadIds"},
@@ -319,6 +319,17 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(0);
}
void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};

View File

@@ -86,6 +86,7 @@ public:
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);

View File

@@ -229,7 +229,7 @@ endif()
create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad)
target_link_libraries(video_core PRIVATE glad xbyak)
if (ENABLE_VULKAN)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)

View File

@@ -457,8 +457,9 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
void Maxwell3D::ProcessQueryGet() {
// TODO(Subv): Support the other query units.
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
"Units other than CROP are unimplemented");
if (regs.query.query_get.unit != Regs::QueryUnit::Crop) {
LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented");
}
switch (regs.query.query_get.operation) {
case Regs::QueryOperation::Release:
@@ -534,8 +535,8 @@ void Maxwell3D::ProcessCounterReset() {
rasterizer.ResetCounter(QueryType::SamplesPassed);
break;
default:
LOG_WARNING(Render_OpenGL, "Unimplemented counter reset={}",
static_cast<int>(regs.counter_reset));
LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
static_cast<int>(regs.counter_reset));
break;
}
}
@@ -592,8 +593,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
system.GPU().GetTicks());
return {};
default:
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
static_cast<u32>(regs.query.query_get.select.Value()));
LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
static_cast<u32>(regs.query.query_get.select.Value()));
return 1;
}
}

View File

@@ -2344,7 +2344,12 @@ private:
return {};
}
Expression MemoryBarrierGL(Operation) {
Expression MemoryBarrierGroup(Operation) {
code.AddLine("groupMemoryBarrier();");
return {};
}
Expression MemoryBarrierGlobal(Operation) {
code.AddLine("memoryBarrier();");
return {};
}
@@ -2591,7 +2596,8 @@ private:
&GLSLDecompiler::ShuffleIndexed,
&GLSLDecompiler::Barrier,
&GLSLDecompiler::MemoryBarrierGL,
&GLSLDecompiler::MemoryBarrierGroup,
&GLSLDecompiler::MemoryBarrierGlobal,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));

View File

@@ -2215,8 +2215,8 @@ private:
return {};
}
Expression MemoryBarrierGL(Operation) {
const auto scope = spv::Scope::Device;
template <spv::Scope scope>
Expression MemoryBarrier(Operation) {
const auto semantics =
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
spv::MemorySemanticsMask::WorkgroupMemory |
@@ -2681,7 +2681,8 @@ private:
&SPIRVDecompiler::ShuffleIndexed,
&SPIRVDecompiler::Barrier,
&SPIRVDecompiler::MemoryBarrierGL,
&SPIRVDecompiler::MemoryBarrier<spv::Scope::Workgroup>,
&SPIRVDecompiler::MemoryBarrier<spv::Scope::Device>,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));

View File

@@ -299,9 +299,19 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::MEMBAR: {
UNIMPLEMENTED_IF(instr.membar.type != Tegra::Shader::MembarType::GL);
UNIMPLEMENTED_IF(instr.membar.unknown != Tegra::Shader::MembarUnknown::Default);
bb.push_back(Operation(OperationCode::MemoryBarrierGL));
const OperationCode type = [instr] {
switch (instr.membar.type) {
case Tegra::Shader::MembarType::CTA:
return OperationCode::MemoryBarrierGroup;
case Tegra::Shader::MembarType::GL:
return OperationCode::MemoryBarrierGlobal;
default:
UNIMPLEMENTED_MSG("MEMBAR type={}", static_cast<int>(instr.membar.type.Value()));
return OperationCode::MemoryBarrierGlobal;
}
}();
bb.push_back(Operation(type));
break;
}
case OpCode::Id::DEPBAR: {

View File

@@ -233,8 +233,9 @@ enum class OperationCode {
ThreadLtMask, /// () -> uint
ShuffleIndexed, /// (uint value, uint index) -> uint
Barrier, /// () -> void
MemoryBarrierGL, /// () -> void
Barrier, /// () -> void
MemoryBarrierGroup, /// () -> void
MemoryBarrierGlobal, /// () -> void
Amount,
};

View File

@@ -652,63 +652,45 @@ private:
**/
std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params,
GPUVAddr gpu_addr) {
const GPUVAddr gpu_addr) {
if (params.target == SurfaceTarget::Texture3D) {
return std::nullopt;
return {};
}
const auto test_modified = [](TSurface& surface) { return surface->IsModified(); };
bool modified = false;
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
if (std::none_of(overlaps.begin(), overlaps.end(), test_modified)) {
LoadSurface(new_surface);
for (const auto& surface : overlaps) {
Unregister(surface);
}
Register(new_surface);
return {{new_surface, new_surface->GetMainView()}};
}
std::size_t passed_tests = 0;
u32 passed_tests = 0;
for (auto& surface : overlaps) {
const SurfaceParams& src_params = surface->GetSurfaceParams();
const auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
if (src_params.is_layered || src_params.num_levels > 1) {
// We send this cases to recycle as they are more complex to handle
return {};
}
const std::size_t candidate_size = surface->GetSizeInBytes();
auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
if (!mipmap_layer) {
continue;
}
const auto [base_layer, base_mipmap] = *mipmap_layer;
if (new_surface->GetMipmapSize(base_mipmap) != surface->GetMipmapSize(0)) {
const auto [layer, mipmap] = *mipmap_layer;
if (new_surface->GetMipmapSize(mipmap) != candidate_size) {
continue;
}
++passed_tests;
// Copy all mipmaps and layers
const u32 block_width = params.GetDefaultBlockWidth();
const u32 block_height = params.GetDefaultBlockHeight();
for (u32 mipmap = base_mipmap; mipmap < base_mipmap + src_params.num_levels; ++mipmap) {
const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap);
const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap);
if (width < block_width || height < block_height) {
// Current APIs forbid copying small compressed textures, avoid errors
break;
}
const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height,
src_params.depth);
ImageCopy(surface, new_surface, copy_params);
}
modified |= surface->IsModified();
// Now we got all the data set up
const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap);
const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap);
const CopyParams copy_params(0, 0, 0, 0, 0, layer, 0, mipmap, width, height, 1);
passed_tests++;
ImageCopy(surface, new_surface, copy_params);
}
if (passed_tests == 0) {
return std::nullopt;
}
if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
return {};
// In Accurate GPU all tests should pass, else we recycle
return std::nullopt;
} else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
return {};
}
const bool modified = std::any_of(overlaps.begin(), overlaps.end(), test_modified);
for (const auto& surface : overlaps) {
Unregister(surface);
}
new_surface->MarkAsModified(modified, Tick());
Register(new_surface);
return {{new_surface, new_surface->GetMainView()}};
@@ -886,9 +868,12 @@ private:
// two things either the candidate surface is a supertexture of the overlap
// or they don't match in any known way.
if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
const std::optional view = TryReconstructSurface(overlaps, params, gpu_addr);
if (view) {
return *view;
if (current_surface->GetGpuAddr() == gpu_addr) {
std::optional<std::pair<TSurface, TView>> view =
TryReconstructSurface(overlaps, params, gpu_addr);
if (view) {
return *view;
}
}
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
MatchTopologyResult::FullMatch);