Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 877a978a22 | |||
| 5933b3ea96 | |||
| 35c095898b | |||
| ea4928393f | |||
| 59c665b28e | |||
| a01832b2e0 | |||
| 1c7696e0d9 | |||
| bf036b46fc | |||
| c5262b1890 | |||
| 4f41fd84ff | |||
| de4f931d13 | |||
| 548fe0fc62 | |||
| 48a526dba1 | |||
| f2536cafe5 | |||
| 9ec9709f68 | |||
| 5af0340066 | |||
| 2c0f831468 | |||
| 56a97647a3 | |||
| a6aa710b84 | |||
| 11f990975c | |||
| ceeff8ebfd | |||
| 7538fa4a67 | |||
| 5bfd810ab5 | |||
| dea1db0414 | |||
| c91d2bac45 | |||
| 351f010cfc | |||
| fe3bf4f075 | |||
| 6a0e258dde | |||
| dd0b17a5f0 | |||
| c8c8412901 | |||
| cd8924520c | |||
| f5f35caf20 | |||
| 1f0c8bfeda | |||
| f96bb2520b | |||
| 09ffa0cb21 | |||
| 244b46af5a | |||
| b4242633ad | |||
| 3a8d38be7e | |||
| 8769604144 | |||
| 205e6d3b97 | |||
| 039e58a984 | |||
| a17dd30057 | |||
| 776ce5d74c | |||
| 387bffda5e | |||
| e5dfbe22ee | |||
| 331c252509 | |||
| f80229b676 | |||
| 97fb6179b9 | |||
| fa97f50bff | |||
| 614de1dba7 | |||
| af51f123fa | |||
| 22d4e10664 | |||
| 8495f1df2a | |||
| 2020ba06e1 | |||
| af9336df57 | |||
| a73c7c73eb | |||
| a81ff6f54c | |||
| f80bc712ea | |||
| 771431f625 | |||
| 19bfbb28ce | |||
| 357bc956ae | |||
| 9aa68212d9 | |||
| c6da772b24 | |||
| 9c2d83cb9c | |||
| 4d61ac08aa | |||
| 08d5663cb8 | |||
| 5102c91256 | |||
| 7149332712 | |||
| 4a32d92e6e |
+1
-136
@@ -1,136 +1 @@
|
||||
# Reporting Issues
|
||||
|
||||
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our [Discord server](https://discordapp.com/invite/u77vRWY), [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
|
||||
|
||||
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
||||
|
||||
# Contributing
|
||||
yuzu is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
|
||||
|
||||
# Using clang format (version 6.0)
|
||||
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
|
||||
|
||||
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
|
||||
* OSX: run `brew install clang-format`.
|
||||
* Linux: use your package manager to get an appropriate binary.
|
||||
|
||||
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
|
||||
|
||||
### General Rules
|
||||
* A lot of code was taken from other projects (e.g. Citra, Dolphin, PPSSPP, Gekko). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
|
||||
* Line width is typically 100 characters. Please do not use 80-characters.
|
||||
* Don't ever introduce new external dependencies into Core
|
||||
* Don't use any platform specific code in Core
|
||||
* Use namespaces often
|
||||
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
|
||||
|
||||
### Naming Rules
|
||||
* Functions: `PascalCase`
|
||||
* Variables: `lower_case_underscored`. Prefix with `g_` if global.
|
||||
* Classes: `PascalCase`
|
||||
* Files and Directories: `lower_case_underscored`
|
||||
* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`)
|
||||
|
||||
### Indentation/Whitespace Style
|
||||
Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
|
||||
|
||||
### Comments
|
||||
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
|
||||
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
|
||||
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
|
||||
|
||||
```cpp
|
||||
// Includes should be sorted lexicographically
|
||||
// STD includes first
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// then, library includes
|
||||
#include <nihstro/shared_binary.h>
|
||||
|
||||
// finally, yuzu includes
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
// each major module is separated
|
||||
#include "video_core/pica.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Example {
|
||||
|
||||
// Namespace contents are not indented
|
||||
|
||||
// Declare globals at the top
|
||||
int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
|
||||
char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
|
||||
|
||||
/// A colorful enum.
|
||||
enum SomeEnum {
|
||||
ColorRed, ///< The color of fire.
|
||||
ColorGreen, ///< The color of grass.
|
||||
ColorBlue, ///< Not actually the color of water.
|
||||
};
|
||||
|
||||
/**
|
||||
* Very important struct that does a lot of stuff.
|
||||
* Note that the asterisks are indented by one space to align to the first line.
|
||||
*/
|
||||
struct Position {
|
||||
int x{}, y{}; // Always intitialize member variables!
|
||||
};
|
||||
|
||||
// Use "typename" rather than "class" here
|
||||
template <typename T>
|
||||
void FooBar() {
|
||||
const std::string some_string{ "prefer uniform initialization" };
|
||||
|
||||
int some_array[]{
|
||||
5,
|
||||
25,
|
||||
7,
|
||||
42,
|
||||
};
|
||||
|
||||
if (note == the_space_after_the_if) {
|
||||
CallAfunction();
|
||||
} else {
|
||||
// Use a space after the // when commenting
|
||||
}
|
||||
|
||||
// Place a single space after the for loop semicolons, prefer pre-increment
|
||||
for (int i{}; i != 25; ++i) {
|
||||
// This is how we write loops
|
||||
}
|
||||
|
||||
DoStuff(this, function, call, takes, up, multiple,
|
||||
lines, like, this);
|
||||
|
||||
if (this || condition_takes_up_multiple &&
|
||||
lines && like && this || everything ||
|
||||
alright || then) {
|
||||
|
||||
// Leave a blank space before the if block body if the condition was continued across
|
||||
// several lines.
|
||||
}
|
||||
|
||||
switch (var) {
|
||||
// No indentation for case label
|
||||
case 1: {
|
||||
int case_var{ var + 3 };
|
||||
DoSomething(case_var);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
DoSomething(var);
|
||||
return;
|
||||
|
||||
default:
|
||||
// Yes, even break for the last case
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<T> you_can_declare, a_few, variables, like_this;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
**The Contributor's Guide has moved to [the Citra wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
|
||||
|
||||
Vendored
+1
-1
Submodule externals/fmt updated: 3e75ad9822...9e554999ce
@@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
return Stream::Format::Multi51Channel16;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const {
|
||||
case Format::Multi51Channel16:
|
||||
return 6;
|
||||
}
|
||||
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ void Stream::Play() {
|
||||
|
||||
void Stream::Stop() {
|
||||
state = State::Stopped;
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
@@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
}
|
||||
|
||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/arm_interface.cpp
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
void ARM_Interface::LogBacktrace() const {
|
||||
VAddr fp = GetReg(29);
|
||||
VAddr lr = GetReg(30);
|
||||
const VAddr sp = GetReg(13);
|
||||
const VAddr pc = GetPC();
|
||||
|
||||
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
|
||||
while (true) {
|
||||
LOG_ERROR(Core_ARM, "{:016X}", lr);
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
}
|
||||
}
|
||||
} // namespace Core
|
||||
@@ -141,6 +141,14 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// fp (= r29) points to the last frame record.
|
||||
/// Note that this is the frame record for the *previous* frame, not the current one.
|
||||
/// Note we need to subtract 4 from our last read to get the proper address
|
||||
/// Frame records are two words long:
|
||||
/// fp+0 : pointer to previous frame record
|
||||
/// fp+8 : value of lr for frame
|
||||
void LogBacktrace() const;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
||||
@@ -92,16 +92,20 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
||||
|
||||
auto address_space = "Unknown";
|
||||
const char* address_space = "Unknown";
|
||||
switch (npdm_header.address_space_type) {
|
||||
case ProgramAddressSpaceType::Is36Bit:
|
||||
address_space = "64-bit (36-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is39Bit:
|
||||
address_space = "64-bit";
|
||||
address_space = "64-bit (39-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32Bit:
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit (no map region)";
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler() = default;
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() = default;
|
||||
|
||||
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
|
||||
server_session->SetHleHandler(shared_from_this());
|
||||
connected_sessions.push_back(std::move(server_session));
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
@@ -27,9 +25,13 @@ class Domain;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class ServerSession;
|
||||
class Thread;
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
* This can be provided to a ServerSession in order to hook into several relevant events
|
||||
@@ -37,7 +39,8 @@ class WritableEvent;
|
||||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
virtual ~SessionRequestHandler() = default;
|
||||
SessionRequestHandler();
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
* Handles a sync request from the emulated application.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
@@ -36,7 +36,6 @@ enum class HandleType : u32 {
|
||||
enum class ResetType {
|
||||
OneShot, ///< Reset automatically on object acquisition
|
||||
Sticky, ///< Never reset automatically
|
||||
Pulse, ///< Reset automatically on wakeup
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
|
||||
@@ -20,6 +20,35 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
*
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
*/
|
||||
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCore(), stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
CodeSet::CodeSet() = default;
|
||||
CodeSet::~CodeSet() = default;
|
||||
@@ -64,7 +93,7 @@ ResultCode Process::ClearSignalState() {
|
||||
|
||||
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_processor = metadata.GetMainThreadCore();
|
||||
ideal_core = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
|
||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||
@@ -86,7 +115,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
vm_manager.LogLayout();
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
|
||||
SetupMainThread(*this, kernel, entry_point, main_thread_priority);
|
||||
}
|
||||
|
||||
void Process::PrepareForTermination() {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -27,6 +26,7 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
|
||||
struct AddressMapping {
|
||||
// Address and size must be page-aligned
|
||||
@@ -168,18 +168,18 @@ public:
|
||||
/// Gets the resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> GetResourceLimit() const;
|
||||
|
||||
/// Gets the default CPU ID for this process
|
||||
u8 GetDefaultProcessorID() const {
|
||||
return ideal_processor;
|
||||
/// Gets the ideal CPU core ID for this process
|
||||
u8 GetIdealCore() const {
|
||||
return ideal_core;
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
||||
u64 GetAllowedProcessorMask() const {
|
||||
/// Gets the bitmask of allowed cores that this process' threads can run on.
|
||||
u64 GetCoreMask() const {
|
||||
return capabilities.GetCoreMask();
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed thread priorities.
|
||||
u64 GetAllowedThreadPriorityMask() const {
|
||||
u64 GetPriorityMask() const {
|
||||
return capabilities.GetPriorityMask();
|
||||
}
|
||||
|
||||
@@ -280,8 +280,8 @@ private:
|
||||
/// Resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||
u8 ideal_processor = 0;
|
||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
||||
u8 ideal_core = 0;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
|
||||
@@ -46,9 +46,6 @@ ResultCode ReadableEvent::Reset() {
|
||||
|
||||
void ReadableEvent::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
|
||||
if (reset_type == ResetType::Pulse)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
+41
-32
@@ -684,6 +684,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
|
||||
.LogBacktrace();
|
||||
ASSERT(false);
|
||||
|
||||
Core::CurrentProcess()->PrepareForTermination();
|
||||
@@ -712,8 +715,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
AllowedCPUCoreMask = 0,
|
||||
AllowedThreadPriorityMask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
@@ -744,8 +747,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
case GetInfoType::MapRegionSize:
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
@@ -771,12 +774,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = process->GetAllowedProcessorMask();
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
*result = process->GetCoreMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = process->GetAllowedThreadPriorityMask();
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
*result = process->GetPriorityMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
@@ -1216,29 +1219,35 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_IDEAL) {
|
||||
// Set the target CPU to the one specified by the process.
|
||||
processor_id = current_process->GetIdealCore();
|
||||
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
|
||||
}
|
||||
|
||||
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
if ((core_mask | (1ULL << processor_id)) != core_mask) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
|
||||
THREADPRIO_LOWEST, priority);
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid thread priority specified ({}). Must be within the range 0-64",
|
||||
priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
processor_id = current_process->GetDefaultProcessorID();
|
||||
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
|
||||
}
|
||||
|
||||
switch (processor_id) {
|
||||
case THREADPROCESSORID_0:
|
||||
case THREADPROCESSORID_1:
|
||||
case THREADPROCESSORID_2:
|
||||
case THREADPROCESSORID_3:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
const std::string name = fmt::format("thread-{:X}", entry_point);
|
||||
@@ -1636,13 +1645,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
||||
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
|
||||
|
||||
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = default_processor_id;
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
mask = 1ULL << core;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
@@ -232,29 +231,6 @@ void Thread::BoostPriority(u32 priority) {
|
||||
current_priority = priority;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
|
||||
stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
||||
context.cpu_registers[0] = result.raw;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ enum ThreadPriority : u32 {
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
@@ -455,17 +455,6 @@ private:
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @return A shared pointer to the main thread
|
||||
*/
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process);
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
|
||||
@@ -68,9 +68,6 @@ void Timer::Clear() {
|
||||
|
||||
void Timer::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
|
||||
if (reset_type == ResetType::Pulse)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void Timer::Signal(int cycles_late) {
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
||||
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
@@ -460,9 +461,17 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
const auto message = msg_queue->PopMessage();
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
|
||||
LOG_ERROR(Service_AM, "Message queue is empty");
|
||||
rb.Push(ERR_NO_MESSAGES);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
return;
|
||||
}
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
@@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
}
|
||||
|
||||
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
|
||||
static_cast<u32>(fatal_type), error_code.raw);
|
||||
switch (fatal_type) {
|
||||
case FatalType::ErrorReportAndScreen:
|
||||
GenerateErrorReport(error_code, info);
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Service::NFP {
|
||||
namespace ErrCodes {
|
||||
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
|
||||
-1); // TODO(ogniK): Find the actual error code
|
||||
}
|
||||
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
} // namespace ErrCodes
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {
|
||||
@@ -292,10 +293,9 @@ private:
|
||||
}
|
||||
|
||||
void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
// We don't need to worry about this since we can just open the file
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
|
||||
}
|
||||
|
||||
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -166,7 +166,7 @@ Layer::~Layer() = default;
|
||||
|
||||
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
|
||||
@@ -264,14 +264,12 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto unknown_u8 = rp.PopRaw<u8>();
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
const auto initial_type = rp.PopRaw<u8>();
|
||||
|
||||
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
CalendarTime calendar_time{};
|
||||
|
||||
const std::time_t time(time_since_epoch);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
@@ -280,16 +278,19 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
|
||||
return;
|
||||
}
|
||||
SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
|
||||
1000};
|
||||
|
||||
LocationName location_name{"UTC"};
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
clock_snapshot.system_posix_time = time_since_epoch;
|
||||
clock_snapshot.network_posix_time = time_since_epoch;
|
||||
clock_snapshot.system_calendar_time = calendar_time;
|
||||
@@ -302,9 +303,10 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
clock_snapshot.network_calendar_info = additional_info;
|
||||
|
||||
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
|
||||
clock_snapshot.location_name = location_name;
|
||||
clock_snapshot.location_name = LocationName{"UTC"};
|
||||
clock_snapshot.clock_auto_adjustment_enabled = 1;
|
||||
clock_snapshot.ipc_u8 = unknown_u8;
|
||||
clock_snapshot.type = initial_type;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
|
||||
@@ -22,7 +22,6 @@ struct CalendarTime {
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
|
||||
|
||||
@@ -30,7 +29,7 @@ struct CalendarAdditionalInfo {
|
||||
u32_le day_of_week;
|
||||
u32_le day_of_year;
|
||||
std::array<u8, 8> name;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 is_dst;
|
||||
s32_le utc_offset;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
|
||||
@@ -42,8 +41,10 @@ struct TimeZoneRule {
|
||||
};
|
||||
|
||||
struct SteadyClockTimePoint {
|
||||
using SourceID = std::array<u8, 16>;
|
||||
|
||||
u64_le value;
|
||||
INSERT_PADDING_WORDS(4);
|
||||
SourceID source_id;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
|
||||
@@ -66,8 +67,9 @@ struct ClockSnapshot {
|
||||
SteadyClockTimePoint steady_clock_timepoint;
|
||||
LocationName location_name;
|
||||
u8 clock_auto_adjustment_enabled;
|
||||
u8 ipc_u8;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u8 type;
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
|
||||
|
||||
|
||||
+124
-83
@@ -19,6 +19,7 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
@@ -32,11 +33,22 @@
|
||||
namespace Service::VI {
|
||||
|
||||
struct DisplayInfo {
|
||||
/// The name of this particular display.
|
||||
char display_name[0x40]{"Default"};
|
||||
u64 unknown_1{1};
|
||||
u64 unknown_2{1};
|
||||
u64 width{1280};
|
||||
u64 height{720};
|
||||
|
||||
/// Whether or not the display has a limited number of layers.
|
||||
u8 has_limited_layers{1};
|
||||
INSERT_PADDING_BYTES(7){};
|
||||
|
||||
/// Indicates the total amount of layers supported by the display.
|
||||
/// @note This is only valid if has_limited_layers is set.
|
||||
u64 max_layers{1};
|
||||
|
||||
/// Maximum width in pixels.
|
||||
u64 width{1920};
|
||||
|
||||
/// Maximum height in pixels.
|
||||
u64 height{1080};
|
||||
};
|
||||
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
|
||||
|
||||
@@ -502,10 +514,12 @@ private:
|
||||
|
||||
void TransactParcel(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
|
||||
u32 flags = rp.Pop<u32>();
|
||||
LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||
static_cast<u32>(transaction), flags);
|
||||
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
@@ -593,9 +607,10 @@ private:
|
||||
|
||||
void AdjustRefcount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
s32 addval = rp.PopRaw<s32>();
|
||||
u32 type = rp.Pop<u32>();
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const s32 addval = rp.PopRaw<s32>();
|
||||
const u32 type = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
|
||||
type);
|
||||
|
||||
@@ -605,11 +620,12 @@ private:
|
||||
|
||||
void GetNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
u32 unknown = rp.Pop<u32>();
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const u32 unknown = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
// TODO(Subv): Find out what this actually is.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -674,11 +690,12 @@ public:
|
||||
|
||||
private:
|
||||
void SetLayerZ(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 z_value = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const u64 z_value = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id,
|
||||
z_value);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -686,8 +703,9 @@ private:
|
||||
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
bool visibility = rp.Pop<bool>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
|
||||
@@ -796,25 +814,27 @@ public:
|
||||
|
||||
private:
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display = rp.Pop<u64>();
|
||||
const u64 display = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 unknown = rp.Pop<u32>();
|
||||
const u32 unknown = rp.Pop<u32>();
|
||||
rp.Skip(1, false);
|
||||
u64 display = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
const u64 display = rp.Pop<u64>();
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display);
|
||||
LOG_WARNING(Service_VI,
|
||||
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
|
||||
unknown, display, aruid);
|
||||
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -822,11 +842,12 @@ private:
|
||||
}
|
||||
|
||||
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 stack = rp.Pop<u32>();
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
const u32 stack = rp.Pop<u32>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack,
|
||||
layer_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -834,8 +855,9 @@ private:
|
||||
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
bool visibility = rp.Pop<bool>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
|
||||
@@ -888,10 +910,23 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 0x40>>();
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
|
||||
}
|
||||
|
||||
void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
OpenDisplayImpl(ctx, "Default");
|
||||
}
|
||||
|
||||
void OpenDisplayImpl(Kernel::HLERequestContext& ctx, std::string_view name) {
|
||||
const auto trim_pos = name.find('\0');
|
||||
|
||||
if (trim_pos != std::string_view::npos) {
|
||||
name.remove_suffix(name.size() - trim_pos);
|
||||
}
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
@@ -901,43 +936,49 @@ private:
|
||||
}
|
||||
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
// This literally does nothing internally in the actual service itself,
|
||||
// and just returns a successful result code regardless of the input.
|
||||
void SetDisplayEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. display_id=0x{:016X}", display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
} else {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
// This only returns the fixed values of 1280x720 and makes no distinguishing
|
||||
// between docked and undocked dimensions. We take the liberty of applying
|
||||
// the resolution scaling factor here.
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
const u32 scaling_mode = rp.Pop<u32>();
|
||||
const u64 unknown = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. scaling_mode=0x{:08X}, unknown=0x{:016X}",
|
||||
scaling_mode, unknown);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -957,19 +998,19 @@ private:
|
||||
}
|
||||
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
const std::string display_name(name_buf.begin(), end);
|
||||
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
|
||||
|
||||
const u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -978,17 +1019,17 @@ private:
|
||||
}
|
||||
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id);
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
@@ -998,22 +1039,22 @@ private:
|
||||
}
|
||||
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1082,9 +1123,9 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
"GetIndirectDisplayTransactionService"},
|
||||
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
|
||||
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
|
||||
{1011, nullptr, "OpenDefaultDisplay"},
|
||||
{1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
|
||||
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
|
||||
{1101, nullptr, "SetDisplayEnabled"},
|
||||
{1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
|
||||
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
|
||||
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
|
||||
{2021, nullptr, "CloseLayer"},
|
||||
|
||||
@@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::BC6H_UF16>,
|
||||
MortonCopy<true, PixelFormat::BC6H_SF16>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::G8R8U>,
|
||||
MortonCopy<true, PixelFormat::G8R8S>,
|
||||
MortonCopy<true, PixelFormat::BGRA8>,
|
||||
MortonCopy<true, PixelFormat::RGBA32F>,
|
||||
MortonCopy<true, PixelFormat::RG32F>,
|
||||
@@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::BC6H_SF16>,
|
||||
// TODO(Subv): Swizzling ASTC formats are not supported
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::G8R8U>,
|
||||
MortonCopy<false, PixelFormat::G8R8S>,
|
||||
MortonCopy<false, PixelFormat::BGRA8>,
|
||||
MortonCopy<false, PixelFormat::RGBA32F>,
|
||||
MortonCopy<false, PixelFormat::RG32F>,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace OpenGL {
|
||||
|
||||
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
|
||||
: RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
|
||||
: RasterizerCache{rasterizer}, stream_buffer(size, true) {}
|
||||
|
||||
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
|
||||
std::size_t alignment, bool cache) {
|
||||
|
||||
@@ -135,27 +135,31 @@ void RasterizerOpenGL::CheckExtensions() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexFormat() {
|
||||
GLuint RasterizerOpenGL::SetupVertexFormat() {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
if (!gpu.dirty_flags.vertex_attrib_format)
|
||||
return;
|
||||
if (!gpu.dirty_flags.vertex_attrib_format) {
|
||||
return state.draw.vertex_array;
|
||||
}
|
||||
gpu.dirty_flags.vertex_attrib_format = false;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
|
||||
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
|
||||
auto& VAO = iter->second;
|
||||
auto& vao_entry = iter->second;
|
||||
|
||||
if (is_cache_miss) {
|
||||
VAO.Create();
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.ApplyVertexBufferState();
|
||||
vao_entry.Create();
|
||||
const GLuint vao = vao_entry.handle;
|
||||
|
||||
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
|
||||
// around.
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
|
||||
// Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob
|
||||
// that fails to properly create the vertex array if it's not bound even after creating it
|
||||
// with glCreateVertexArrays
|
||||
state.draw.vertex_array = vao;
|
||||
state.ApplyVertexArrayState();
|
||||
|
||||
glVertexArrayElementBuffer(vao, buffer_cache.GetHandle());
|
||||
|
||||
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
|
||||
// Enables the first 16 vertex attributes always, as we don't know which ones are actually
|
||||
@@ -163,7 +167,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
|
||||
// for now to avoid OpenGL errors.
|
||||
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
|
||||
// assume every shader uses them all.
|
||||
for (unsigned index = 0; index < 16; ++index) {
|
||||
for (u32 index = 0; index < 16; ++index) {
|
||||
const auto& attrib = regs.vertex_attrib_format[index];
|
||||
|
||||
// Ignore invalid attributes.
|
||||
@@ -178,28 +182,29 @@ void RasterizerOpenGL::SetupVertexFormat() {
|
||||
|
||||
ASSERT(buffer.IsEnabled());
|
||||
|
||||
glEnableVertexAttribArray(index);
|
||||
glEnableVertexArrayAttrib(vao, index);
|
||||
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
|
||||
attrib.type ==
|
||||
Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
|
||||
glVertexAttribIFormat(index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib), attrib.offset);
|
||||
glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib), attrib.offset);
|
||||
} else {
|
||||
glVertexAttribFormat(index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
glVertexArrayAttribFormat(
|
||||
vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
}
|
||||
glVertexAttribBinding(index, attrib.buffer);
|
||||
glVertexArrayAttribBinding(vao, index, attrib.buffer);
|
||||
}
|
||||
}
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
// Rebinding the VAO invalidates the vertex buffer bindings.
|
||||
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
|
||||
|
||||
state.draw.vertex_array = vao_entry.handle;
|
||||
return vao_entry.handle;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
@@ -217,7 +222,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
if (!vertex_array.IsEnabled())
|
||||
continue;
|
||||
|
||||
Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
const Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
|
||||
|
||||
ASSERT(end > start);
|
||||
@@ -225,21 +230,18 @@ void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
|
||||
|
||||
// Bind the vertex array to the buffer at the current offset.
|
||||
glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
glVertexArrayVertexBuffer(vao, index, buffer_cache.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
|
||||
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
|
||||
// Enable vertex buffer instancing with the specified divisor.
|
||||
glVertexBindingDivisor(index, vertex_array.divisor);
|
||||
glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
|
||||
} else {
|
||||
// Disable the vertex buffer instancing.
|
||||
glVertexBindingDivisor(index, 0);
|
||||
glVertexArrayBindingDivisor(vao, index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
|
||||
gpu.dirty_flags.vertex_array = 0;
|
||||
}
|
||||
|
||||
@@ -689,9 +691,6 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
std::size_t buffer_size = CalculateVertexArraysSize();
|
||||
|
||||
// Add space for index buffer (keeping in mind non-core primitives)
|
||||
@@ -721,8 +720,9 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
SetupVertexFormat();
|
||||
SetupVertexBuffer();
|
||||
const GLuint vao = SetupVertexFormat();
|
||||
SetupVertexBuffer(vao);
|
||||
|
||||
DrawParameters params = SetupDraw();
|
||||
SetupShaders(params.primitive_mode);
|
||||
|
||||
@@ -1014,8 +1014,11 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
|
||||
state.texture_units[current_bindpoint].target = surface->Target();
|
||||
const GLuint handle =
|
||||
entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
|
||||
const GLenum target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
|
||||
state.texture_units[current_bindpoint].texture = handle;
|
||||
state.texture_units[current_bindpoint].target = target;
|
||||
state.texture_units[current_bindpoint].swizzle.r =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
state.texture_units[current_bindpoint].swizzle.g =
|
||||
|
||||
@@ -209,8 +209,10 @@ private:
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
void SetupVertexFormat();
|
||||
void SetupVertexBuffer();
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
|
||||
DrawParameters SetupDraw();
|
||||
|
||||
|
||||
@@ -44,6 +44,17 @@ struct FormatTuple {
|
||||
bool compressed;
|
||||
};
|
||||
|
||||
static void ApplyTextureDefaults(GLenum target, u32 max_mip_level) {
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_mip_level - 1);
|
||||
if (max_mip_level == 1) {
|
||||
glTexParameterf(target, GL_TEXTURE_LOD_BIAS, 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
|
||||
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
|
||||
@@ -288,8 +299,6 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
||||
true}, // BC6H_SF16
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
|
||||
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // G8R8S
|
||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
||||
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
|
||||
@@ -532,6 +541,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
|
||||
gl_internal_format = format_tuple.internal_format;
|
||||
gl_is_compressed = format_tuple.compressed;
|
||||
|
||||
if (!format_tuple.compressed) {
|
||||
// Only pre-create the texture for non-compressed textures.
|
||||
switch (params.target) {
|
||||
@@ -560,15 +572,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
|
||||
}
|
||||
}
|
||||
|
||||
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL,
|
||||
params.max_mip_level - 1);
|
||||
if (params.max_mip_level == 1) {
|
||||
glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0);
|
||||
}
|
||||
ApplyTextureDefaults(SurfaceTargetToGL(params.target), params.max_mip_level);
|
||||
|
||||
LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
|
||||
SurfaceParams::SurfaceTargetName(params.target));
|
||||
@@ -620,18 +624,6 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
|
||||
constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)};
|
||||
for (std::size_t y = 0; y < height; ++y) {
|
||||
for (std::size_t x = 0; x < width; ++x) {
|
||||
const std::size_t offset{bpp * (y * width + x)};
|
||||
const u8 temp{data[offset]};
|
||||
data[offset] = data[offset + 1];
|
||||
data[offset + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
|
||||
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
|
||||
@@ -664,12 +656,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
||||
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
|
||||
ConvertS8Z24ToZ24S8(data, width, height, false);
|
||||
break;
|
||||
|
||||
case PixelFormat::G8R8U:
|
||||
case PixelFormat::G8R8S:
|
||||
// Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
|
||||
ConvertG8R8ToR8G8(data, width, height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,8 +667,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
||||
static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
|
||||
u32 width, u32 height) {
|
||||
switch (pixel_format) {
|
||||
case PixelFormat::G8R8U:
|
||||
case PixelFormat::G8R8S:
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
case PixelFormat::ASTC_2D_8X8:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
@@ -886,6 +870,31 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
void CachedSurface::EnsureTextureView() {
|
||||
if (texture_view.handle != 0)
|
||||
return;
|
||||
// Compressed texture are not being created with immutable storage
|
||||
UNIMPLEMENTED_IF(gl_is_compressed);
|
||||
|
||||
const GLenum target{TargetLayer()};
|
||||
|
||||
texture_view.Create();
|
||||
glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, 0,
|
||||
params.max_mip_level, 0, 1);
|
||||
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
const auto& old_tex = cur_state.texture_units[0];
|
||||
SCOPE_EXIT({
|
||||
cur_state.texture_units[0] = old_tex;
|
||||
cur_state.Apply();
|
||||
});
|
||||
cur_state.texture_units[0].texture = texture_view.handle;
|
||||
cur_state.texture_units[0].target = target;
|
||||
cur_state.Apply();
|
||||
|
||||
ApplyTextureDefaults(target, params.max_mip_level);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
|
||||
if (params.type == SurfaceType::Fill)
|
||||
|
||||
@@ -293,10 +293,31 @@ public:
|
||||
return texture;
|
||||
}
|
||||
|
||||
const OGLTexture& TextureLayer() {
|
||||
if (params.is_layered) {
|
||||
return Texture();
|
||||
}
|
||||
EnsureTextureView();
|
||||
return texture_view;
|
||||
}
|
||||
|
||||
GLenum Target() const {
|
||||
return gl_target;
|
||||
}
|
||||
|
||||
GLenum TargetLayer() const {
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
return GL_TEXTURE_1D_ARRAY;
|
||||
case SurfaceTarget::Texture2D:
|
||||
return GL_TEXTURE_2D_ARRAY;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
}
|
||||
return Target();
|
||||
}
|
||||
|
||||
const SurfaceParams& GetSurfaceParams() const {
|
||||
return params;
|
||||
}
|
||||
@@ -311,11 +332,16 @@ public:
|
||||
private:
|
||||
void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
|
||||
|
||||
void EnsureTextureView();
|
||||
|
||||
OGLTexture texture;
|
||||
OGLTexture texture_view;
|
||||
std::vector<std::vector<u8>> gl_buffer;
|
||||
SurfaceParams params;
|
||||
GLenum gl_target;
|
||||
std::size_t cached_size_in_bytes;
|
||||
SurfaceParams params{};
|
||||
GLenum gl_target{};
|
||||
GLenum gl_internal_format{};
|
||||
bool gl_is_compressed{};
|
||||
std::size_t cached_size_in_bytes{};
|
||||
};
|
||||
|
||||
class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
|
||||
|
||||
@@ -117,7 +117,7 @@ void OGLBuffer::Create() {
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glGenBuffers(1, &handle);
|
||||
glCreateBuffers(1, &handle);
|
||||
}
|
||||
|
||||
void OGLBuffer::Release() {
|
||||
@@ -126,7 +126,6 @@ void OGLBuffer::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteBuffers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@@ -152,7 +151,7 @@ void OGLVertexArray::Create() {
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glGenVertexArrays(1, &handle);
|
||||
glCreateVertexArrays(1, &handle);
|
||||
}
|
||||
|
||||
void OGLVertexArray::Release() {
|
||||
|
||||
@@ -83,8 +83,6 @@ OpenGLState::OpenGLState() {
|
||||
draw.read_framebuffer = 0;
|
||||
draw.draw_framebuffer = 0;
|
||||
draw.vertex_array = 0;
|
||||
draw.vertex_buffer = 0;
|
||||
draw.uniform_buffer = 0;
|
||||
draw.shader_program = 0;
|
||||
draw.program_pipeline = 0;
|
||||
|
||||
@@ -505,7 +503,6 @@ void OpenGLState::ApplySamplers() const {
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyFramebufferState() const {
|
||||
// Framebuffer
|
||||
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
|
||||
}
|
||||
@@ -514,16 +511,10 @@ void OpenGLState::ApplyFramebufferState() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyVertexBufferState() const {
|
||||
// Vertex array
|
||||
void OpenGLState::ApplyVertexArrayState() const {
|
||||
if (draw.vertex_array != cur_state.draw.vertex_array) {
|
||||
glBindVertexArray(draw.vertex_array);
|
||||
}
|
||||
|
||||
// Vertex buffer
|
||||
if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDepthClamp() const {
|
||||
@@ -543,11 +534,7 @@ void OpenGLState::ApplyDepthClamp() const {
|
||||
|
||||
void OpenGLState::Apply() const {
|
||||
ApplyFramebufferState();
|
||||
ApplyVertexBufferState();
|
||||
// Uniform buffer
|
||||
if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
|
||||
}
|
||||
ApplyVertexArrayState();
|
||||
|
||||
// Shader program
|
||||
if (draw.shader_program != cur_state.draw.shader_program) {
|
||||
@@ -638,16 +625,6 @@ OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (draw.vertex_buffer == handle) {
|
||||
draw.vertex_buffer = 0;
|
||||
}
|
||||
if (draw.uniform_buffer == handle) {
|
||||
draw.uniform_buffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (draw.vertex_array == handle) {
|
||||
draw.vertex_array = 0;
|
||||
|
||||
@@ -154,8 +154,6 @@ public:
|
||||
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
|
||||
GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
|
||||
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
|
||||
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
|
||||
GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
|
||||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
|
||||
} draw;
|
||||
@@ -206,10 +204,10 @@ public:
|
||||
}
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply() const;
|
||||
/// Apply only the state afecting the framebuffer
|
||||
/// Apply only the state affecting the framebuffer
|
||||
void ApplyFramebufferState() const;
|
||||
/// Apply only the state afecting the vertex buffer
|
||||
void ApplyVertexBufferState() const;
|
||||
/// Apply only the state affecting the vertex array
|
||||
void ApplyVertexArrayState() const;
|
||||
/// Set the initial OpenGL state
|
||||
static void ApplyDefaultState();
|
||||
/// Resets any references to the given resource
|
||||
@@ -217,7 +215,6 @@ public:
|
||||
OpenGLState& ResetSampler(GLuint handle);
|
||||
OpenGLState& ResetProgram(GLuint handle);
|
||||
OpenGLState& ResetPipeline(GLuint handle);
|
||||
OpenGLState& ResetBuffer(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
void EmulateViewportWithScissor();
|
||||
|
||||
@@ -15,13 +15,12 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
|
||||
: gl_target(target), buffer_size(size) {
|
||||
OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent)
|
||||
: buffer_size(size) {
|
||||
gl_buffer.Create();
|
||||
glBindBuffer(gl_target, gl_buffer.handle);
|
||||
|
||||
GLsizeiptr allocate_size = size;
|
||||
if (target == GL_ARRAY_BUFFER) {
|
||||
if (vertex_data_usage) {
|
||||
// On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer
|
||||
// read position is near the end and is an out-of-bound access to the vertex buffer. This is
|
||||
// probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
|
||||
@@ -35,18 +34,17 @@ OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coh
|
||||
coherent = prefer_coherent;
|
||||
const GLbitfield flags =
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
|
||||
glBufferStorage(gl_target, allocate_size, nullptr, flags);
|
||||
mapped_ptr = static_cast<u8*>(glMapBufferRange(
|
||||
gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
|
||||
glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags);
|
||||
mapped_ptr = static_cast<u8*>(glMapNamedBufferRange(
|
||||
gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
|
||||
} else {
|
||||
glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW);
|
||||
glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
OGLStreamBuffer::~OGLStreamBuffer() {
|
||||
if (persistent) {
|
||||
glBindBuffer(gl_target, gl_buffer.handle);
|
||||
glUnmapBuffer(gl_target);
|
||||
glUnmapNamedBuffer(gl_buffer.handle);
|
||||
}
|
||||
gl_buffer.Release();
|
||||
}
|
||||
@@ -74,7 +72,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
|
||||
invalidate = true;
|
||||
|
||||
if (persistent) {
|
||||
glUnmapBuffer(gl_target);
|
||||
glUnmapNamedBuffer(gl_buffer.handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +82,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
|
||||
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
|
||||
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
mapped_ptr = static_cast<u8*>(
|
||||
glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags));
|
||||
glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags));
|
||||
mapped_offset = buffer_pos;
|
||||
}
|
||||
|
||||
@@ -95,11 +93,11 @@ void OGLStreamBuffer::Unmap(GLsizeiptr size) {
|
||||
ASSERT(size <= mapped_size);
|
||||
|
||||
if (!coherent && size > 0) {
|
||||
glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size);
|
||||
glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size);
|
||||
}
|
||||
|
||||
if (!persistent) {
|
||||
glUnmapBuffer(gl_target);
|
||||
glUnmapNamedBuffer(gl_buffer.handle);
|
||||
}
|
||||
|
||||
buffer_pos += size;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace OpenGL {
|
||||
|
||||
class OGLStreamBuffer : private NonCopyable {
|
||||
public:
|
||||
explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false);
|
||||
explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false);
|
||||
~OGLStreamBuffer();
|
||||
|
||||
GLuint GetHandle() const;
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
|
||||
private:
|
||||
OGLBuffer gl_buffer;
|
||||
GLenum gl_target;
|
||||
|
||||
bool coherent = false;
|
||||
bool persistent = false;
|
||||
|
||||
@@ -245,20 +245,20 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
|
||||
// Generate VAO
|
||||
vertex_array.Create();
|
||||
|
||||
state.draw.vertex_array = vertex_array.handle;
|
||||
state.draw.vertex_buffer = vertex_buffer.handle;
|
||||
state.draw.uniform_buffer = 0;
|
||||
state.Apply();
|
||||
|
||||
// Attach vertex data to VAO
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
|
||||
(GLvoid*)offsetof(ScreenRectVertex, position));
|
||||
glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
|
||||
(GLvoid*)offsetof(ScreenRectVertex, tex_coord));
|
||||
glEnableVertexAttribArray(attrib_position);
|
||||
glEnableVertexAttribArray(attrib_tex_coord);
|
||||
glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
|
||||
glVertexArrayAttribFormat(vertex_array.handle, attrib_position, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, position));
|
||||
glVertexArrayAttribFormat(vertex_array.handle, attrib_tex_coord, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, tex_coord));
|
||||
glVertexArrayAttribBinding(vertex_array.handle, attrib_position, 0);
|
||||
glVertexArrayAttribBinding(vertex_array.handle, attrib_tex_coord, 0);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, attrib_position);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, attrib_tex_coord);
|
||||
glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
|
||||
sizeof(ScreenRectVertex));
|
||||
|
||||
// Allocate textures for the screen
|
||||
screen_info.texture.resource.Create();
|
||||
@@ -370,14 +370,12 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
state.texture_units[0].texture = screen_info.display_texture;
|
||||
state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
|
||||
// Workaround brigthness problems in SMO by enabling sRGB in the final output
|
||||
// if it has been used in the frame
|
||||
// Needed because of this bug in QT
|
||||
// QTBUG-50987
|
||||
// if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
|
||||
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
|
||||
state.Apply();
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
// restore default state
|
||||
// Restore default state
|
||||
state.framebuffer_srgb.enabled = false;
|
||||
state.texture_units[0].texture = 0;
|
||||
state.Apply();
|
||||
|
||||
@@ -196,11 +196,14 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
case Tegra::Texture::TextureFormat::G8R8:
|
||||
// TextureFormat::G8R8 is actually ordered red then green, as such we can use
|
||||
// PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath
|
||||
// of the Wild, which uses this format to render the hearts on the UI.
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return PixelFormat::G8R8U;
|
||||
return PixelFormat::RG8U;
|
||||
case Tegra::Texture::ComponentType::SNORM:
|
||||
return PixelFormat::G8R8S;
|
||||
return PixelFormat::RG8S;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
|
||||
+41
-51
@@ -38,57 +38,55 @@ enum class PixelFormat {
|
||||
BC6H_UF16 = 20,
|
||||
BC6H_SF16 = 21,
|
||||
ASTC_2D_4X4 = 22,
|
||||
G8R8U = 23,
|
||||
G8R8S = 24,
|
||||
BGRA8 = 25,
|
||||
RGBA32F = 26,
|
||||
RG32F = 27,
|
||||
R32F = 28,
|
||||
R16F = 29,
|
||||
R16U = 30,
|
||||
R16S = 31,
|
||||
R16UI = 32,
|
||||
R16I = 33,
|
||||
RG16 = 34,
|
||||
RG16F = 35,
|
||||
RG16UI = 36,
|
||||
RG16I = 37,
|
||||
RG16S = 38,
|
||||
RGB32F = 39,
|
||||
RGBA8_SRGB = 40,
|
||||
RG8U = 41,
|
||||
RG8S = 42,
|
||||
RG32UI = 43,
|
||||
R32UI = 44,
|
||||
ASTC_2D_8X8 = 45,
|
||||
ASTC_2D_8X5 = 46,
|
||||
ASTC_2D_5X4 = 47,
|
||||
BGRA8_SRGB = 48,
|
||||
DXT1_SRGB = 49,
|
||||
DXT23_SRGB = 50,
|
||||
DXT45_SRGB = 51,
|
||||
BC7U_SRGB = 52,
|
||||
ASTC_2D_4X4_SRGB = 53,
|
||||
ASTC_2D_8X8_SRGB = 54,
|
||||
ASTC_2D_8X5_SRGB = 55,
|
||||
ASTC_2D_5X4_SRGB = 56,
|
||||
ASTC_2D_5X5 = 57,
|
||||
ASTC_2D_5X5_SRGB = 58,
|
||||
ASTC_2D_10X8 = 59,
|
||||
ASTC_2D_10X8_SRGB = 60,
|
||||
BGRA8 = 23,
|
||||
RGBA32F = 24,
|
||||
RG32F = 25,
|
||||
R32F = 26,
|
||||
R16F = 27,
|
||||
R16U = 28,
|
||||
R16S = 29,
|
||||
R16UI = 30,
|
||||
R16I = 31,
|
||||
RG16 = 32,
|
||||
RG16F = 33,
|
||||
RG16UI = 34,
|
||||
RG16I = 35,
|
||||
RG16S = 36,
|
||||
RGB32F = 37,
|
||||
RGBA8_SRGB = 38,
|
||||
RG8U = 39,
|
||||
RG8S = 40,
|
||||
RG32UI = 41,
|
||||
R32UI = 42,
|
||||
ASTC_2D_8X8 = 43,
|
||||
ASTC_2D_8X5 = 44,
|
||||
ASTC_2D_5X4 = 45,
|
||||
BGRA8_SRGB = 46,
|
||||
DXT1_SRGB = 47,
|
||||
DXT23_SRGB = 48,
|
||||
DXT45_SRGB = 49,
|
||||
BC7U_SRGB = 50,
|
||||
ASTC_2D_4X4_SRGB = 51,
|
||||
ASTC_2D_8X8_SRGB = 52,
|
||||
ASTC_2D_8X5_SRGB = 53,
|
||||
ASTC_2D_5X4_SRGB = 54,
|
||||
ASTC_2D_5X5 = 55,
|
||||
ASTC_2D_5X5_SRGB = 56,
|
||||
ASTC_2D_10X8 = 57,
|
||||
ASTC_2D_10X8_SRGB = 58,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// Depth formats
|
||||
Z32F = 61,
|
||||
Z16 = 62,
|
||||
Z32F = 59,
|
||||
Z16 = 60,
|
||||
|
||||
MaxDepthFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 63,
|
||||
S8Z24 = 64,
|
||||
Z32FS8 = 65,
|
||||
Z24S8 = 61,
|
||||
S8Z24 = 62,
|
||||
Z32FS8 = 63,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -149,8 +147,6 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -232,8 +228,6 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -309,8 +303,6 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
@@ -386,8 +378,6 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
128, // BC6H_UF16
|
||||
128, // BC6H_SF16
|
||||
128, // ASTC_2D_4X4
|
||||
16, // G8R8U
|
||||
16, // G8R8S
|
||||
32, // BGRA8
|
||||
128, // RGBA32F
|
||||
64, // RG32F
|
||||
|
||||
@@ -470,6 +470,8 @@ void Config::ReadValues() {
|
||||
qt_config->value("enable_discord_presence", true).toBool();
|
||||
UISettings::values.screenshot_resolution_factor =
|
||||
static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
|
||||
UISettings::values.select_user_on_boot =
|
||||
qt_config->value("select_user_on_boot", false).toBool();
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
||||
@@ -680,7 +682,7 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first));
|
||||
qt_config->beginWriteArray("disabled");
|
||||
for (std::size_t j = 0; j < elem.second.size(); ++j) {
|
||||
qt_config->setArrayIndex(j);
|
||||
qt_config->setArrayIndex(static_cast<int>(j));
|
||||
qt_config->setValue("d", QString::fromStdString(elem.second[j]));
|
||||
}
|
||||
qt_config->endArray();
|
||||
@@ -693,6 +695,7 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
|
||||
qt_config->setValue("screenshot_resolution_factor",
|
||||
UISettings::values.screenshot_resolution_factor);
|
||||
qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot);
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>461</width>
|
||||
<height>659</height>
|
||||
<width>382</width>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -15,51 +15,71 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="ConfigureGeneral" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGameList" name="gameListTab">
|
||||
<attribute name="title">
|
||||
<string>Game List</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureSystem" name="systemTab">
|
||||
<attribute name="title">
|
||||
<string>System</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureInputSimple" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGraphics" name="graphicsTab">
|
||||
<attribute name="title">
|
||||
<string>Graphics</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureAudio" name="audioTab">
|
||||
<attribute name="title">
|
||||
<string>Audio</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureDebug" name="debugTab">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureWeb" name="webTab">
|
||||
<attribute name="title">
|
||||
<string>Web</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="selectorList">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="ConfigureGeneral" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGameList" name="gameListTab">
|
||||
<attribute name="title">
|
||||
<string>Game List</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureSystem" name="systemTab">
|
||||
<attribute name="title">
|
||||
<string>System</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureInputSimple" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGraphics" name="graphicsTab">
|
||||
<attribute name="title">
|
||||
<string>Graphics</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureAudio" name="audioTab">
|
||||
<attribute name="title">
|
||||
<string>Audio</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureDebug" name="debugTab">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureWeb" name="webTab">
|
||||
<attribute name="title">
|
||||
<string>Web</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
@@ -77,12 +97,6 @@
|
||||
<header>configuration/configure_general.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGameList</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_gamelist.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureSystem</class>
|
||||
<extends>QWidget</extends>
|
||||
@@ -101,12 +115,6 @@
|
||||
<header>configuration/configure_debug.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureInputSimple</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_input_simple.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGraphics</class>
|
||||
<extends>QWidget</extends>
|
||||
@@ -119,6 +127,18 @@
|
||||
<header>configuration/configure_web.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGameList</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_gamelist.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureInputSimple</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_input_simple.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QHash>
|
||||
#include <QListWidgetItem>
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
@@ -13,6 +15,13 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry
|
||||
ui->setupUi(this);
|
||||
ui->generalTab->PopulateHotkeyList(registry);
|
||||
this->setConfiguration();
|
||||
this->PopulateSelectionList();
|
||||
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
|
||||
&ConfigureDialog::UpdateVisibleTabs);
|
||||
|
||||
adjustSize();
|
||||
|
||||
ui->selectorList->setCurrentRow(0);
|
||||
}
|
||||
|
||||
ConfigureDialog::~ConfigureDialog() = default;
|
||||
@@ -30,3 +39,37 @@ void ConfigureDialog::applyConfiguration() {
|
||||
ui->webTab->applyConfiguration();
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureDialog::PopulateSelectionList() {
|
||||
const std::array<std::pair<QString, QStringList>, 4> items{
|
||||
{{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}},
|
||||
{tr("System"), {tr("System"), tr("Audio")}},
|
||||
{tr("Graphics"), {tr("Graphics")}},
|
||||
{tr("Controls"), {tr("Input")}}}};
|
||||
|
||||
for (const auto& entry : items) {
|
||||
auto* const item = new QListWidgetItem(entry.first);
|
||||
item->setData(Qt::UserRole, entry.second);
|
||||
|
||||
ui->selectorList->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureDialog::UpdateVisibleTabs() {
|
||||
const auto items = ui->selectorList->selectedItems();
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const std::map<QString, QWidget*> widgets = {
|
||||
{tr("General"), ui->generalTab}, {tr("System"), ui->systemTab},
|
||||
{tr("Input"), ui->inputTab}, {tr("Graphics"), ui->graphicsTab},
|
||||
{tr("Audio"), ui->audioTab}, {tr("Debug"), ui->debugTab},
|
||||
{tr("Web"), ui->webTab}, {tr("Game List"), ui->gameListTab}};
|
||||
|
||||
ui->tabWidget->clear();
|
||||
|
||||
const QStringList tabs = items[0]->data(Qt::UserRole).toStringList();
|
||||
|
||||
for (const auto& tab : tabs)
|
||||
ui->tabWidget->addTab(widgets.find(tab)->second, tab);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void UpdateVisibleTabs();
|
||||
void PopulateSelectionList();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
void ConfigureGeneral::setConfiguration() {
|
||||
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
|
||||
@@ -42,6 +43,7 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
|
||||
@@ -38,6 +38,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_user_on_boot">
|
||||
<property name="text">
|
||||
<string>Prompt for user on game boot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -120,7 +120,7 @@ void ConfigurePerGameGeneral::loadConfiguration() {
|
||||
|
||||
QPixmap map;
|
||||
const auto bytes = control.second->ReadAllBytes();
|
||||
map.loadFromData(bytes.data(), bytes.size());
|
||||
map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
@@ -130,7 +130,7 @@ void ConfigurePerGameGeneral::loadConfiguration() {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map;
|
||||
map.loadFromData(bytes.data(), bytes.size());
|
||||
map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
|
||||
@@ -182,8 +182,6 @@ QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
|
||||
return tr("one shot");
|
||||
case Kernel::ResetType::Sticky:
|
||||
return tr("sticky");
|
||||
case Kernel::ResetType::Pulse:
|
||||
return tr("pulse");
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
@@ -293,8 +291,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
|
||||
QString processor;
|
||||
switch (thread.GetProcessorID()) {
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT:
|
||||
processor = tr("default");
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_IDEAL:
|
||||
processor = tr("ideal");
|
||||
break;
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_0:
|
||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_1:
|
||||
|
||||
+31
-21
@@ -687,10 +687,26 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::SelectAndSetCurrentUser() {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
if (dialog.GetStatus()) {
|
||||
Settings::values.current_user = static_cast<s32>(dialog.GetIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::BootGame(const QString& filename) {
|
||||
LOG_INFO(Frontend, "yuzu starting...");
|
||||
StoreRecentFile(filename); // Put the filename on top of the list
|
||||
|
||||
if (UISettings::values.select_user_on_boot) {
|
||||
SelectAndSetCurrentUser();
|
||||
}
|
||||
|
||||
if (!LoadROM(filename))
|
||||
return;
|
||||
|
||||
@@ -827,31 +843,25 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
ASSERT(program_id != 0);
|
||||
|
||||
Service::Account::ProfileManager manager{};
|
||||
const auto user_ids = manager.GetAllUsers();
|
||||
QStringList list;
|
||||
for (const auto& user_id : user_ids) {
|
||||
if (user_id == Service::Account::UUID{})
|
||||
continue;
|
||||
Service::Account::ProfileBase base;
|
||||
if (!manager.GetProfileBase(user_id, base))
|
||||
continue;
|
||||
const auto select_profile = [this]() -> s32 {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size())));
|
||||
}
|
||||
if (!dialog.GetStatus()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
const auto index_string =
|
||||
QInputDialog::getItem(this, tr("Select User"),
|
||||
tr("Please select the user's save data you would like to open."),
|
||||
list, Settings::values.current_user, false, &ok);
|
||||
if (!ok)
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
const auto index = list.indexOf(index_string);
|
||||
ASSERT(index != -1 && index < 8);
|
||||
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(index);
|
||||
ASSERT(user_id);
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
|
||||
|
||||
@@ -128,6 +128,8 @@ private:
|
||||
void ShowTelemetryCallout();
|
||||
void SetDiscordEnabled(bool state);
|
||||
|
||||
void SelectAndSetCurrentUser();
|
||||
|
||||
/**
|
||||
* Stores the filename in the recently loaded files list.
|
||||
* The new filename is stored at the beginning of the recently loaded files list.
|
||||
|
||||
@@ -40,6 +40,8 @@ struct Values {
|
||||
bool confirm_before_closing;
|
||||
bool first_start;
|
||||
|
||||
bool select_user_on_boot;
|
||||
|
||||
// Discord RPC
|
||||
bool enable_discord_presence;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user