Compare commits

...

16 Commits

Author SHA1 Message Date
Fernando Sahmkow
935c5439da GPUMemoryManager: Fix mapping ranges. 2021-07-14 22:53:23 +02:00
Fernando Sahmkow
73b563d637 NVDRV: Fix Nvmap and MapBufferEx in nvhost_as_gpu. 2021-07-14 22:52:23 +02:00
Fernando Sahmkow
bce8490f05 nvhost-as-gpu: correct Remap method when no object is present.
Credits to ByLaws from Skyline-emu for pointing this out.
2021-07-14 22:52:23 +02:00
Ameer J
f2599534f8 Merge pull request #6599 from german77/disable_rumble
npad: Disable vibration check if disabled
2021-07-13 16:11:59 -04:00
bunnei
00ce8eff65 Merge pull request #6574 from lioncash/i18n
qt/main: Make title string more i18n-friendly
2021-07-12 22:12:09 -07:00
bunnei
af79911017 Merge pull request #6593 from german77/no_sdl
input_common: Fix build with SDL disabled
2021-07-12 22:11:39 -07:00
bunnei
b8becb0608 Merge pull request #6615 from ReinUsesLisp/httplib-debug-warnings
boxcat,web_service: Silence -Wmaybe-uninitialized when including httplib.h
2021-07-12 22:11:19 -07:00
bunnei
81b2ba1479 Merge pull request #6618 from ReinUsesLisp/bad-ranges
content_archive: Remove unnecessary include to <ranges>
2021-07-12 22:10:50 -07:00
bunnei
7d464f73c9 Merge pull request #6571 from Kelebek1/Mix
audio_core: Replace NaN mix volume samples with silence
2021-07-12 22:09:05 -07:00
ReinUsesLisp
1ef64112b3 content_archive: Remove unnecessary include to <ranges>
Fixes build issues on clang.
2021-07-12 03:37:56 -03:00
ReinUsesLisp
4503a4ac43 web_service: Silence -Wmaybe-uninitialized on httplib.h 2021-07-12 03:30:45 -03:00
ReinUsesLisp
69214ef678 boxcat: Silence -Wmaybe-uninitialized in httplib.h 2021-07-12 03:30:45 -03:00
german77
ed5f1a45b7 npad: Disable vibration check if disabled 2021-07-10 20:06:07 -05:00
german77
289f59dabd input_common: Fix build with sdl disabled 2021-07-10 20:02:02 -05:00
Lioncash
5c541b0b42 qt/main: Make title string more i18n-friendly
Currently, whether or not the title is 32-bit or 64-bit was being
appended as a suffix to the title, which is fine for left-to-right
languages, but may not always fly so smoothly with some right-to-left
languages.

We also weren't marking that portion of the string as translatable,
which prevents translators from translating part of the title string.
2021-07-08 15:06:16 -04:00
Kelebek1
7905eb0254 Replace NaN mix volume samples with silence.
Fixes Xenoblade Chronicles 2 blowing out the audio.
2021-07-08 17:42:15 +01:00
12 changed files with 222 additions and 46 deletions

View File

@@ -42,6 +42,15 @@ void ApplyMix(std::span<s32> output, std::span<const s32> input, s32 gain, s32 s
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta,
s32 sample_count) {
// XC2 passes in NaN mix volumes, causing further issues as we handle everything as s32 rather
// than float, so the NaN propogation is lost. As the samples get further modified for
// volume etc, they can get out of NaN range, so a later heuristic for catching this is
// more difficult. Handle it here by setting these samples to silence.
if (std::isnan(gain)) {
gain = 0.0f;
delta = 0.0f;
}
s32 x = 0;
for (s32 i = 0; i < sample_count; i++) {
x = static_cast<s32>(static_cast<float>(input[i]) * gain);

View File

@@ -5,7 +5,6 @@
#include <algorithm>
#include <cstring>
#include <optional>
#include <ranges>
#include <utility>
#include "common/logging/log.h"

View File

@@ -7,6 +7,9 @@
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#endif
#include <httplib.h>
#include <mbedtls/sha256.h>

View File

@@ -941,6 +941,11 @@ void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_de
void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
std::size_t device_index) {
if (!Settings::values.vibration_enabled.GetValue()) {
vibration_devices_mounted[npad_index][device_index] = false;
return;
}
if (vibrations[npad_index][device_index]) {
vibration_devices_mounted[npad_index][device_index] =
vibrations[npad_index][device_index]->GetStatus() == 1;

View File

@@ -146,6 +146,14 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
if (entry.nvmap_handle == 0) {
// If nvmap handle is null, we should unmap instead.
const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
const auto size{static_cast<u64>(entry.pages) << 0x10};
system.GPU().MemoryManager().Unmap(offset, size);
continue;
}
const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
@@ -179,24 +187,8 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
std::memcpy(output.data(), &params, output.size());
return NvResult::InvalidState;
}
// The real nvservices doesn't make a distinction between handles and ids, and
// object can only have one handle and it will be the same as its id. Assert that this is the
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
auto& gpu = system.GPU();
u64 page_size{params.page_size};
if (!page_size) {
page_size = object->align;
}
if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
@@ -223,6 +215,23 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
}
}
const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
std::memcpy(output.data(), &params, output.size());
return NvResult::InvalidState;
}
// The real nvservices doesn't make a distinction between handles and ids, and
// object can only have one handle and it will be the same as its id. Assert that this is the
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
u64 page_size{params.page_size};
if (!page_size) {
page_size = object->align;
}
// We can only map objects that have already been assigned a CPU address.
ASSERT(object->status == nvmap::Object::Status::Allocated);

View File

@@ -11,11 +11,7 @@
namespace Service::Nvidia::Devices {
nvmap::nvmap(Core::System& system_) : nvdevice{system_} {
// Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
// represent this.
CreateObject(0);
}
nvmap::nvmap(Core::System& system_) : nvdevice{system_} {}
nvmap::~nvmap() = default;

View File

@@ -56,10 +56,10 @@ public:
private:
/// Id to use for the next handle that is created.
u32 next_handle = 0;
u32 next_handle = 1;
/// Id to use for the next object that is created.
u32 next_id = 0;
u32 next_id = 1;
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;

View File

@@ -294,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() {
impl->udp->ReloadSockets();
}
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
Polling::DeviceType type) const {
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
[maybe_unused]] Polling::DeviceType type) const {
#ifdef HAVE_SDL2
return impl->sdl->GetPollers(type);
#else

View File

@@ -38,13 +38,110 @@ GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std
return gpu_addr;
}
GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
if (it != map_ranges.end() && it->first == gpu_addr) {
it->second = size;
} else {
map_ranges.insert(it, MapRange{gpu_addr, size});
MemoryManager::MemoryRegion::MemoryRegion() = default;
MemoryManager::MemoryRegion::MemoryRegion(GPUVAddr gpu_addr_, size_t size_)
: address{gpu_addr_}, size{size_} {}
void MemoryManager::InsertRange(GPUVAddr gpu_addr, size_t size) {
if (map_ranges.empty()) {
map_ranges.insert(MemoryRegion{gpu_addr, size});
return;
}
const GPUVAddr gpu_addr_end = gpu_addr + size;
auto clear_and_map = [&](std::set<MemoryRegion>::iterator it_start) {
auto it_end = map_ranges.upper_bound(MemoryRegion{gpu_addr_end - 1, 1});
GPUVAddr alloc_gpu_addr = gpu_addr;
GPUVAddr alloc_gpu_addr_end = gpu_addr_end;
while (it_start != it_end) {
const GPUVAddr local_addr_end = it_start->address + it_start->size;
if (it_start->address < gpu_addr) {
if (local_addr_end <= gpu_addr) {
it_start++;
continue;
}
alloc_gpu_addr = std::min(alloc_gpu_addr, it_start->address);
}
if (local_addr_end > gpu_addr_end) {
alloc_gpu_addr_end = std::max(alloc_gpu_addr_end, local_addr_end);
}
it_start = map_ranges.erase(it_start);
//@Note: do I have to recalculate the upper_bound or are the iterators safe after
// delete?
it_end = map_ranges.upper_bound(MemoryRegion{gpu_addr_end - 1, 1});
}
size_t new_size = alloc_gpu_addr_end - alloc_gpu_addr;
map_ranges.insert(MemoryRegion{alloc_gpu_addr, new_size});
};
auto it_first = map_ranges.upper_bound(MemoryRegion{gpu_addr, size});
--it_first;
if (it_first != map_ranges.end()) {
clear_and_map(it_first);
} else {
auto it = map_ranges.lower_bound(MemoryRegion{gpu_addr, size});
if (it == map_ranges.end() || it->address >= gpu_addr_end) {
map_ranges.insert(MemoryRegion{gpu_addr, size});
return;
}
clear_and_map(it);
}
}
void MemoryManager::RemoveRange(GPUVAddr gpu_addr, size_t size) {
if (map_ranges.empty()) {
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
return;
}
const GPUVAddr gpu_addr_end = gpu_addr + size;
auto clear_and_unmap = [&](std::set<MemoryRegion>::iterator it_start) {
auto it_end = map_ranges.upper_bound(MemoryRegion{gpu_addr_end - 1, 1});
while (it_start != it_end) {
const GPUVAddr local_addr_end = it_start->address + it_start->size;
if (it_start->address < gpu_addr) {
if (local_addr_end <= gpu_addr) {
it_start++;
continue;
}
MemoryRegion new_region{};
new_region.address = it_start->address;
new_region.size = local_addr_end - gpu_addr;
map_ranges.erase(it_start);
auto pair = map_ranges.insert(new_region);
it_start = pair.first;
it_end = map_ranges.upper_bound(MemoryRegion{gpu_addr_end - 1, 1});
it_start++;
continue;
}
if (local_addr_end > gpu_addr_end) {
MemoryRegion new_region{};
new_region.address = gpu_addr_end;
new_region.size = local_addr_end - gpu_addr_end;
map_ranges.insert(new_region);
break;
}
it_start = map_ranges.erase(it_start);
//@Note: do I have to recalculate the upper_bound or are the iterators safe after
// delete?
it_end = map_ranges.upper_bound(MemoryRegion{gpu_addr_end - 1, 1});
}
};
auto it_first = map_ranges.upper_bound(MemoryRegion{gpu_addr, size});
--it_first;
if (it_first != map_ranges.end()) {
clear_and_unmap(it_first);
} else {
auto it = map_ranges.lower_bound(MemoryRegion{gpu_addr, size});
if (it == map_ranges.end() || it->address >= gpu_addr_end) {
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
return;
}
clear_and_unmap(it);
}
}
GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
InsertRange(gpu_addr, size);
ASSERT(IsFullyMapped(gpu_addr, size));
return UpdateRange(gpu_addr, cpu_addr, size);
}
@@ -62,13 +159,8 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
if (size == 0) {
return;
}
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
if (it != map_ranges.end()) {
ASSERT(it->first == gpu_addr);
map_ranges.erase(it);
} else {
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
}
RemoveRange(gpu_addr, size);
ASSERT(IsFullyUnmapped(gpu_addr, size));
const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
@@ -258,10 +350,39 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
return system.Memory().GetPointer(*address);
}
size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
bool MemoryManager::IsFullyMapped(GPUVAddr gpu_addr, size_t size) const {
if (map_ranges.empty()) {
return false;
}
auto it = map_ranges.upper_bound(MemoryRegion{gpu_addr, size});
--it;
return it->second - (gpu_addr - it->first);
if (it == map_ranges.end()) {
return false;
}
const GPUVAddr gpu_addr_end = gpu_addr + size;
return it->address <= gpu_addr && (it->address + it->size) >= gpu_addr_end;
}
bool MemoryManager::IsFullyUnmapped(GPUVAddr gpu_addr, size_t size) const {
if (map_ranges.empty()) {
return true;
}
auto it = map_ranges.upper_bound(MemoryRegion{gpu_addr, size});
const GPUVAddr gpu_addr_end = gpu_addr + size;
if (it != map_ranges.end() && it->address < gpu_addr_end) {
return false;
}
--it;
if (it == map_ranges.end()) {
return true;
}
return (it->address + it->size) <= gpu_addr;
}
size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
auto it = map_ranges.upper_bound(MemoryRegion{gpu_addr, 1});
--it;
return it->size - (gpu_addr - it->address);
}
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {

View File

@@ -6,6 +6,7 @@
#include <map>
#include <optional>
#include <set>
#include <vector>
#include "common/common_types.h"
@@ -155,6 +156,13 @@ private:
void FlushRegion(GPUVAddr gpu_addr, size_t size) const;
void InsertRange(GPUVAddr gpu_addr, size_t size);
void RemoveRange(GPUVAddr gpu_addr, size_t size);
// For debugging only
bool IsFullyMapped(GPUVAddr gpu_addr, size_t size) const;
bool IsFullyUnmapped(GPUVAddr gpu_addr, size_t size) const;
[[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
return (gpu_addr >> page_bits) & page_table_mask;
}
@@ -175,8 +183,22 @@ private:
std::vector<PageEntry> page_table;
using MapRange = std::pair<GPUVAddr, size_t>;
std::vector<MapRange> map_ranges;
struct MemoryRegion {
MemoryRegion();
MemoryRegion(GPUVAddr gpu_addr, size_t size);
bool operator<(const MemoryRegion& other) const {
return address < other.address;
}
bool operator==(const MemoryRegion&) const = default;
bool operator!=(const MemoryRegion&) const = default;
GPUVAddr address{};
size_t size{};
};
std::set<MemoryRegion> map_ranges;
std::vector<std::pair<VAddr, std::size_t>> cache_invalidate_queue;
};

View File

@@ -8,7 +8,17 @@
#include <string>
#include <fmt/format.h>
#ifdef __GNUC__
#pragma GCC diagnostic push
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#endif
#include <httplib.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include "common/logging/log.h"
#include "web_service/web_backend.h"

View File

@@ -1431,8 +1431,10 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
std::filesystem::path{filename.toStdU16String()}.filename());
}
const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess();
const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)";
title_name += instruction_set_suffix;
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
.arg(QString::fromStdString(title_name), instruction_set_suffix)
.toStdString();
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor();
UpdateWindowTitle(title_name, title_version, gpu_vendor);