Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61f6eaad45 | |||
| 8714d40a77 | |||
| 8dc9f35baf | |||
| 883eb1a1a1 | |||
| 0fc596de6e | |||
| a056d8de16 | |||
| bfa973a62b | |||
| 3d7c284e0f | |||
| b6ae48966d | |||
| 0e8a3bf3e5 | |||
| 344d15f61e | |||
| ff5a0f370c | |||
| 7b069252f8 | |||
| 46c3047283 | |||
| ae7dfa93be | |||
| deb1b54eed | |||
| 39c66abd91 | |||
| c4374d0d41 | |||
| 35d40b74b3 | |||
| c414ebaa9c | |||
| e07dfc4da3 | |||
| 63d30133f8 | |||
| f1e4f3fc0c | |||
| 468576284d | |||
| 4d66ca97e5 | |||
| c1a3d19897 | |||
| 654b77d2ec | |||
| ece5287843 | |||
| b0ab803ce8 | |||
| 28bb248db6 | |||
| 442a1cc021 | |||
| 76ca2a5f82 | |||
| a993df1ee2 |
@@ -7,9 +7,9 @@ ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
@@ -4,9 +4,9 @@ $GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
|
||||
$GITREV = $(git show -s --format='%h')
|
||||
|
||||
if ("$BUILD_NAME" -eq "mainline") {
|
||||
$RELEASE_DIST = "yuzu-windows-msvc-$BUILD_NAME"
|
||||
} else {
|
||||
$RELEASE_DIST = "yuzu-windows-msvc"
|
||||
} else {
|
||||
$RELEASE_DIST = "yuzu-windows-msvc-$BUILD_NAME"
|
||||
}
|
||||
|
||||
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
|
||||
@@ -7,9 +7,9 @@ ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||
COMPRESSION_FLAGS="-czvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
@@ -56,7 +56,7 @@ std::string GetLastErrorMsg();
|
||||
namespace Common {
|
||||
|
||||
constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
return a | b << 8 | c << 16 | d << 24;
|
||||
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -58,8 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
if (!writable_event) {
|
||||
// Create event if not provided
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
|
||||
"HLE Pause Event: " + reason);
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
|
||||
writable_event = pair.writable;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,11 +32,6 @@ enum class HandleType : u32 {
|
||||
ServerSession,
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
Automatic, ///< Reset automatically on object acquisition
|
||||
Manual, ///< Never reset automatically
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
public:
|
||||
explicit Object(KernelCore& kernel);
|
||||
|
||||
@@ -20,15 +20,13 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||
|
||||
void ReadableEvent::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::Automatic) {
|
||||
signaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadableEvent::Signal() {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
if (!signaled) {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
};
|
||||
}
|
||||
|
||||
void ReadableEvent::Clear() {
|
||||
|
||||
@@ -27,10 +27,6 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
@@ -55,8 +51,7 @@ private:
|
||||
|
||||
void Signal();
|
||||
|
||||
ResetType reset_type;
|
||||
bool signaled;
|
||||
bool signaled{};
|
||||
|
||||
std::string name; ///< Name of event (optional)
|
||||
};
|
||||
|
||||
@@ -2099,7 +2099,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
const auto [readable_event, writable_event] =
|
||||
WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
|
||||
WritableEvent::CreateEventPair(kernel, "CreateEvent");
|
||||
|
||||
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace Kernel {
|
||||
WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {}
|
||||
WritableEvent::~WritableEvent() = default;
|
||||
|
||||
EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name) {
|
||||
EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) {
|
||||
SharedPtr<WritableEvent> writable_event(new WritableEvent(kernel));
|
||||
SharedPtr<ReadableEvent> readable_event(new ReadableEvent(kernel));
|
||||
|
||||
@@ -24,7 +23,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_typ
|
||||
writable_event->readable = readable_event;
|
||||
readable_event->name = name + ":Readable";
|
||||
readable_event->signaled = false;
|
||||
readable_event->reset_type = reset_type;
|
||||
|
||||
return {std::move(readable_event), std::move(writable_event)};
|
||||
}
|
||||
@@ -33,10 +31,6 @@ SharedPtr<ReadableEvent> WritableEvent::GetReadableEvent() const {
|
||||
return readable;
|
||||
}
|
||||
|
||||
ResetType WritableEvent::GetResetType() const {
|
||||
return readable->reset_type;
|
||||
}
|
||||
|
||||
void WritableEvent::Signal() {
|
||||
readable->Signal();
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ public:
|
||||
/**
|
||||
* Creates an event
|
||||
* @param kernel The kernel instance to create this event under.
|
||||
* @param reset_type ResetType describing how to create event
|
||||
* @param name Optional name of event
|
||||
*/
|
||||
static EventPair CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name = "Unknown");
|
||||
static EventPair CreateEventPair(KernelCore& kernel, std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "WritableEvent";
|
||||
@@ -44,8 +42,6 @@ public:
|
||||
|
||||
SharedPtr<ReadableEvent> GetReadableEvent() const;
|
||||
|
||||
ResetType GetResetType() const;
|
||||
|
||||
void Signal();
|
||||
void Clear();
|
||||
bool IsSignaled() const;
|
||||
|
||||
@@ -289,8 +289,8 @@ ISelfController::ISelfController(Core::System& system,
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
launchable_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ISelfController:LaunchableEvent");
|
||||
|
||||
// This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
|
||||
// called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
|
||||
@@ -298,7 +298,7 @@ ISelfController::ISelfController(Core::System& system,
|
||||
// suspended if the event has previously been created by a call to
|
||||
// GetAccumulatedSuspendedTickChangedEvent.
|
||||
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
accumulated_suspended_tick_changed_event.writable->Signal();
|
||||
}
|
||||
|
||||
@@ -523,10 +523,10 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
|
||||
on_new_message =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
@@ -1091,7 +1091,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
}
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
state_changed_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
@@ -67,8 +67,8 @@ AOC_U::AOC_U(Core::System& system)
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
aoc_change_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() = default;
|
||||
|
||||
@@ -65,8 +65,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
|
||||
buffer_event =
|
||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||
audio_params.channel_count, std::move(unique_name),
|
||||
|
||||
@@ -47,8 +47,8 @@ public:
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
system_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
|
||||
system_event =
|
||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||
system.CoreTiming(), audren_params, system_event.writable, instance_number);
|
||||
}
|
||||
@@ -180,17 +180,17 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
buffer_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IAudioOutBufferReleasedEvent");
|
||||
|
||||
// Should be similar to audio_output_device_switch_event
|
||||
audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent");
|
||||
kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
|
||||
|
||||
// Should only be signalled when an audio output device has been changed, example: speaker
|
||||
// to headset
|
||||
audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
|
||||
kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace Service::BCAT {
|
||||
ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
|
||||
std::string_view event_name) {
|
||||
event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
|
||||
kernel, std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
|
||||
|
||||
@@ -34,8 +34,7 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(kernel, "BT:RegisterEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,14 +57,12 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ScanEvent");
|
||||
connection_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ConfigEvent");
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ScanEvent");
|
||||
connection_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConfigEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
notification_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
|
||||
system.Kernel(), "INotificationService:NotifyEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -174,7 +174,7 @@ void Controller_NPad::OnInit() {
|
||||
auto& kernel = system.Kernel();
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
|
||||
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -26,8 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)), system(system) {
|
||||
auto& kernel = system.Kernel();
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IUser:NFCTagDetected");
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:NFCTagDetected");
|
||||
}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
@@ -66,10 +65,9 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:DeactivateEvent");
|
||||
availability_change_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IUser:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -69,10 +69,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event2");
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event2");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -141,8 +141,7 @@ public:
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
finished_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -40,8 +40,7 @@ Module::Module(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
|
||||
events_interface.events[i] =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, event_label);
|
||||
events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
|
||||
events_interface.status[i] = EventState::Free;
|
||||
events_interface.registered[i] = false;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
|
||||
: id(id), layer_id(layer_id) {
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"BufferQueue NativeHandle");
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Service::VI {
|
||||
|
||||
Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
|
||||
auto& kernel = system.Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
vsync_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
Display::~Display() = default;
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
@@ -30,7 +34,7 @@ public:
|
||||
using BufferInfo = std::pair<const TBufferType*, u64>;
|
||||
|
||||
BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
|
||||
bool is_written = false) {
|
||||
bool is_written = false, bool use_fast_cbuf = false) {
|
||||
std::lock_guard lock{mutex};
|
||||
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
@@ -43,9 +47,13 @@ public:
|
||||
// Cache management is a big overhead, so only cache entries with a given size.
|
||||
// TODO: Figure out which size is the best for given games.
|
||||
constexpr std::size_t max_stream_size = 0x800;
|
||||
if (size < max_stream_size) {
|
||||
if (use_fast_cbuf || size < max_stream_size) {
|
||||
if (!is_written && !IsRegionWritten(cache_addr, cache_addr + size - 1)) {
|
||||
return StreamBufferUpload(host_ptr, size, alignment);
|
||||
if (use_fast_cbuf) {
|
||||
return ConstBufferUpload(host_ptr, size);
|
||||
} else {
|
||||
return StreamBufferUpload(host_ptr, size, alignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +160,10 @@ protected:
|
||||
virtual void CopyBlock(const TBuffer& src, const TBuffer& dst, std::size_t src_offset,
|
||||
std::size_t dst_offset, std::size_t size) = 0;
|
||||
|
||||
virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Register an object into the cache
|
||||
void Register(const MapInterval& new_map, bool inherit_written = false) {
|
||||
const CacheAddr cache_ptr = new_map->GetStart();
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128));
|
||||
|
||||
CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t size)
|
||||
@@ -26,11 +30,22 @@ CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t siz
|
||||
CachedBufferBlock::~CachedBufferBlock() = default;
|
||||
|
||||
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
std::size_t stream_size)
|
||||
: VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>{
|
||||
rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {}
|
||||
const Device& device, std::size_t stream_size)
|
||||
: GenericBufferCache{rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {
|
||||
if (!device.HasFastBufferSubData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OGLBufferCache::~OGLBufferCache() = default;
|
||||
static constexpr auto size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize);
|
||||
glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
|
||||
for (const GLuint cbuf : cbufs) {
|
||||
glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
OGLBufferCache::~OGLBufferCache() {
|
||||
glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
|
||||
}
|
||||
|
||||
Buffer OGLBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) {
|
||||
return std::make_shared<CachedBufferBlock>(cache_addr, size);
|
||||
@@ -69,4 +84,12 @@ void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t
|
||||
static_cast<GLsizeiptr>(size));
|
||||
}
|
||||
|
||||
OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer,
|
||||
std::size_t size) {
|
||||
DEBUG_ASSERT(cbuf_cursor < std::size(cbufs));
|
||||
const GLuint& cbuf = cbufs[cbuf_cursor++];
|
||||
glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer);
|
||||
return {&cbuf, 0};
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
@@ -18,12 +20,14 @@ class System;
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device;
|
||||
class OGLStreamBuffer;
|
||||
class RasterizerOpenGL;
|
||||
|
||||
class CachedBufferBlock;
|
||||
|
||||
using Buffer = std::shared_ptr<CachedBufferBlock>;
|
||||
using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
|
||||
|
||||
class CachedBufferBlock : public VideoCommon::BufferBlock {
|
||||
public:
|
||||
@@ -38,14 +42,18 @@ private:
|
||||
OGLBuffer gl_buffer{};
|
||||
};
|
||||
|
||||
class OGLBufferCache final : public VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer> {
|
||||
class OGLBufferCache final : public GenericBufferCache {
|
||||
public:
|
||||
explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
std::size_t stream_size);
|
||||
const Device& device, std::size_t stream_size);
|
||||
~OGLBufferCache();
|
||||
|
||||
const GLuint* GetEmptyBuffer(std::size_t) override;
|
||||
|
||||
void Acquire() noexcept {
|
||||
cbuf_cursor = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override;
|
||||
|
||||
@@ -61,6 +69,14 @@ protected:
|
||||
|
||||
void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
|
||||
std::size_t dst_offset, std::size_t size) override;
|
||||
|
||||
BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override;
|
||||
|
||||
private:
|
||||
std::size_t cbuf_cursor = 0;
|
||||
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers *
|
||||
Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram>
|
||||
cbufs;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -51,8 +51,11 @@ bool HasExtension(const std::vector<std::string_view>& images, std::string_view
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device() {
|
||||
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const std::vector extensions = GetExtensions();
|
||||
|
||||
const bool is_nvidia = vendor == "NVIDIA Corporation";
|
||||
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
@@ -64,6 +67,7 @@ Device::Device() {
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = TestComponentIndexingBug();
|
||||
has_precise_bug = TestPreciseBug();
|
||||
has_fast_buffer_sub_data = is_nvidia;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
return has_precise_bug;
|
||||
}
|
||||
|
||||
bool HasFastBufferSubData() const {
|
||||
return has_fast_buffer_sub_data;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestComponentIndexingBug();
|
||||
@@ -69,6 +73,7 @@ private:
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
bool has_precise_bug{};
|
||||
bool has_fast_buffer_sub_data{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -67,7 +67,7 @@ static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buf
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device},
|
||||
system{system}, screen_info{info}, buffer_cache{*this, system, STREAM_BUFFER_SIZE} {
|
||||
system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
state.draw.shader_program = 0;
|
||||
state.Apply();
|
||||
@@ -558,6 +558,8 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
SyncPolygonOffset();
|
||||
SyncAlphaTest();
|
||||
|
||||
buffer_cache.Acquire();
|
||||
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
@@ -879,7 +881,8 @@ void RasterizerOpenGL::SetupConstBuffer(const Tegra::Engines::ConstBufferInfo& b
|
||||
const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4));
|
||||
|
||||
const auto alignment = device.GetUniformBufferAlignment();
|
||||
const auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment);
|
||||
const auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment, false,
|
||||
device.HasFastBufferSubData());
|
||||
bind_ubo_pushbuffer.Push(cbuf, offset, size);
|
||||
}
|
||||
|
||||
@@ -935,10 +938,9 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
|
||||
if (!entry.IsBindless()) {
|
||||
return maxwell3d.GetStageTexture(stage, entry.GetOffset());
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
Tegra::Engines::ShaderType shader_type = static_cast<Tegra::Engines::ShaderType>(stage);
|
||||
tex_handle.raw = maxwell3d.AccessConstBuffer32(shader_type, cbuf.first, cbuf.second);
|
||||
const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage);
|
||||
const Tegra::Texture::TextureHandle tex_handle =
|
||||
maxwell3d.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset());
|
||||
return maxwell3d.GetTextureInfo(tex_handle);
|
||||
}();
|
||||
|
||||
@@ -966,10 +968,8 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
|
||||
if (!entry.IsBindless()) {
|
||||
return compute.GetTexture(entry.GetOffset());
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
tex_handle.raw = compute.AccessConstBuffer32(Tegra::Engines::ShaderType::Compute,
|
||||
cbuf.first, cbuf.second);
|
||||
const Tegra::Texture::TextureHandle tex_handle = compute.AccessConstBuffer32(
|
||||
Tegra::Engines::ShaderType::Compute, entry.GetBuffer(), entry.GetOffset());
|
||||
return compute.GetTextureInfo(tex_handle);
|
||||
}();
|
||||
|
||||
@@ -1012,10 +1012,8 @@ void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
|
||||
if (!entry.IsBindless()) {
|
||||
return compute.GetTexture(entry.GetOffset()).tic;
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
tex_handle.raw = compute.AccessConstBuffer32(Tegra::Engines::ShaderType::Compute,
|
||||
cbuf.first, cbuf.second);
|
||||
const Tegra::Texture::TextureHandle tex_handle = compute.AccessConstBuffer32(
|
||||
Tegra::Engines::ShaderType::Compute, entry.GetBuffer(), entry.GetOffset());
|
||||
return compute.GetTextureInfo(tex_handle).tic;
|
||||
}();
|
||||
SetupImage(bindpoint, tic, entry);
|
||||
|
||||
@@ -394,7 +394,8 @@ Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
|
||||
params.disk_cache.SaveRaw(ShaderDiskCacheRaw(
|
||||
params.unique_identifier, GetProgramType(program_type), program_code, program_code_b));
|
||||
|
||||
ConstBufferLocker locker(GetEnginesShaderType(GetProgramType(program_type)));
|
||||
ConstBufferLocker locker(GetEnginesShaderType(GetProgramType(program_type)),
|
||||
params.system.GPU().Maxwell3D());
|
||||
const ShaderIR ir(program_code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker);
|
||||
// TODO(Rodrigo): Handle VertexA shaders
|
||||
// std::optional<ShaderIR> ir_b;
|
||||
@@ -410,7 +411,8 @@ Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, Prog
|
||||
params.disk_cache.SaveRaw(
|
||||
ShaderDiskCacheRaw(params.unique_identifier, ProgramType::Compute, code));
|
||||
|
||||
ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute);
|
||||
ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute,
|
||||
params.system.GPU().KeplerCompute());
|
||||
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker);
|
||||
return std::shared_ptr<CachedShader>(new CachedShader(
|
||||
params, ProgramType::Compute, GLShader::GetEntries(ir), std::move(code), {}));
|
||||
|
||||
@@ -735,7 +735,7 @@ private:
|
||||
|
||||
void DeclareImages() {
|
||||
const auto& images{ir.GetImages()};
|
||||
for (const auto& [offset, image] : images) {
|
||||
for (const auto& image : images) {
|
||||
std::string qualifier = "coherent volatile";
|
||||
if (image.IsRead() && !image.IsWritten()) {
|
||||
qualifier += " readonly";
|
||||
@@ -1670,7 +1670,7 @@ private:
|
||||
|
||||
const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
|
||||
return {GenerateTexture(operation, "Gather",
|
||||
{TextureArgument{type, meta->component}, TextureAoffi{}}) +
|
||||
{TextureAoffi{}, TextureArgument{type, meta->component}}) +
|
||||
GetSwizzle(meta->element),
|
||||
Type::Float};
|
||||
}
|
||||
@@ -2466,16 +2466,16 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
|
||||
entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
|
||||
cbuf.first);
|
||||
}
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& [offset, image] : ir.GetImages()) {
|
||||
entries.images.emplace_back(image);
|
||||
}
|
||||
for (const auto& [base, usage] : ir.GetGlobalMemory()) {
|
||||
entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, usage.is_read,
|
||||
usage.is_written);
|
||||
}
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& image : ir.GetImages()) {
|
||||
entries.images.emplace_back(image);
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
entries.shader_length = ir.GetLength();
|
||||
return entries;
|
||||
|
||||
@@ -82,10 +82,9 @@ private:
|
||||
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffers;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::vector<SamplerEntry> bindless_samplers;
|
||||
std::vector<ImageEntry> images;
|
||||
std::vector<GlobalMemoryEntry> global_memory_entries;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::vector<ImageEntry> images;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
std::size_t shader_length{};
|
||||
};
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
namespace {
|
||||
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
|
||||
@@ -68,15 +70,15 @@ struct CFGRebuildState {
|
||||
const ProgramCode& program_code;
|
||||
ConstBufferLocker& locker;
|
||||
u32 start{};
|
||||
std::vector<BlockInfo> block_info{};
|
||||
std::list<u32> inspect_queries{};
|
||||
std::list<Query> queries{};
|
||||
std::unordered_map<u32, u32> registered{};
|
||||
std::set<u32> labels{};
|
||||
std::map<u32, u32> ssy_labels{};
|
||||
std::map<u32, u32> pbk_labels{};
|
||||
std::unordered_map<u32, BlockStack> stacks{};
|
||||
ASTManager* manager;
|
||||
std::vector<BlockInfo> block_info;
|
||||
std::list<u32> inspect_queries;
|
||||
std::list<Query> queries;
|
||||
std::unordered_map<u32, u32> registered;
|
||||
std::set<u32> labels;
|
||||
std::map<u32, u32> ssy_labels;
|
||||
std::map<u32, u32> pbk_labels;
|
||||
std::unordered_map<u32, BlockStack> stacks;
|
||||
ASTManager* manager{};
|
||||
};
|
||||
|
||||
enum class BlockCollision : u32 { None, Found, Inside };
|
||||
@@ -109,7 +111,7 @@ BlockInfo& CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) {
|
||||
}
|
||||
|
||||
Pred GetPredicate(u32 index, bool negated) {
|
||||
return static_cast<Pred>(index + (negated ? 8 : 0));
|
||||
return static_cast<Pred>(static_cast<u64>(index) + (negated ? 8ULL : 0ULL));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,15 +138,13 @@ struct BranchIndirectInfo {
|
||||
s32 relative_position{};
|
||||
};
|
||||
|
||||
std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state,
|
||||
u32 start_address, u32 current_position) {
|
||||
const u32 shader_start = state.start;
|
||||
u32 pos = current_position;
|
||||
BranchIndirectInfo result{};
|
||||
u64 track_register = 0;
|
||||
struct BufferInfo {
|
||||
u32 index;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
// Step 0 Get BRX Info
|
||||
const Instruction instr = {state.program_code[pos]};
|
||||
std::optional<std::pair<s32, u64>> GetBRXInfo(const CFGRebuildState& state, u32& pos) {
|
||||
const Instruction instr = state.program_code[pos];
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
if (opcode->get().GetId() != OpCode::Id::BRX) {
|
||||
return std::nullopt;
|
||||
@@ -152,86 +152,94 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState&
|
||||
if (instr.brx.constant_buffer != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
track_register = instr.gpr8.Value();
|
||||
result.relative_position = instr.brx.GetBranchExtend();
|
||||
pos--;
|
||||
bool found_track = false;
|
||||
--pos;
|
||||
return std::make_pair(instr.brx.GetBranchExtend(), instr.gpr8.Value());
|
||||
}
|
||||
|
||||
// Step 1 Track LDC
|
||||
while (pos >= shader_start) {
|
||||
if (IsSchedInstruction(pos, shader_start)) {
|
||||
pos--;
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = {state.program_code[pos]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
if (opcode->get().GetId() == OpCode::Id::LD_C) {
|
||||
if (instr.gpr0.Value() == track_register &&
|
||||
instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) {
|
||||
result.buffer = instr.cbuf36.index.Value();
|
||||
result.offset = static_cast<u32>(instr.cbuf36.GetOffset());
|
||||
track_register = instr.gpr8.Value();
|
||||
pos--;
|
||||
found_track = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos--;
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
return std::nullopt;
|
||||
}
|
||||
found_track = false;
|
||||
|
||||
// Step 2 Track SHL
|
||||
while (pos >= shader_start) {
|
||||
if (IsSchedInstruction(pos, shader_start)) {
|
||||
pos--;
|
||||
template <typename Result, typename TestCallable, typename PackCallable>
|
||||
// requires std::predicate<TestCallable, Instruction, const OpCode::Matcher&>
|
||||
// requires std::invocable<PackCallable, Instruction, const OpCode::Matcher&>
|
||||
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
||||
PackCallable pack) {
|
||||
for (; pos >= state.start; --pos) {
|
||||
if (IsSchedInstruction(pos, state.start)) {
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = state.program_code[pos];
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
if (opcode->get().GetId() == OpCode::Id::SHL_IMM) {
|
||||
if (instr.gpr0.Value() == track_register) {
|
||||
track_register = instr.gpr8.Value();
|
||||
pos--;
|
||||
found_track = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos--;
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
return std::nullopt;
|
||||
}
|
||||
found_track = false;
|
||||
|
||||
// Step 3 Track IMNMX
|
||||
while (pos >= shader_start) {
|
||||
if (IsSchedInstruction(pos, shader_start)) {
|
||||
pos--;
|
||||
if (!opcode) {
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = state.program_code[pos];
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
if (opcode->get().GetId() == OpCode::Id::IMNMX_IMM) {
|
||||
if (instr.gpr0.Value() == track_register) {
|
||||
track_register = instr.gpr8.Value();
|
||||
result.entries = instr.alu.GetSignedImm20_20() + 1;
|
||||
pos--;
|
||||
found_track = true;
|
||||
break;
|
||||
}
|
||||
if (test(instr, opcode->get())) {
|
||||
--pos;
|
||||
return std::make_optional(pack(instr, opcode->get()));
|
||||
}
|
||||
pos--;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state, u32& pos,
|
||||
u64 brx_tracked_register) {
|
||||
return TrackInstruction<std::pair<BufferInfo, u64>>(
|
||||
state, pos,
|
||||
[brx_tracked_register](auto instr, const auto& opcode) {
|
||||
return opcode.GetId() == OpCode::Id::LD_C &&
|
||||
instr.gpr0.Value() == brx_tracked_register &&
|
||||
instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single;
|
||||
},
|
||||
[](auto instr, const auto& opcode) {
|
||||
const BufferInfo info = {static_cast<u32>(instr.cbuf36.index.Value()),
|
||||
static_cast<u32>(instr.cbuf36.GetOffset())};
|
||||
return std::make_pair(info, instr.gpr8.Value());
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos,
|
||||
u64 ldc_tracked_register) {
|
||||
return TrackInstruction<u64>(state, pos,
|
||||
[ldc_tracked_register](auto instr, const auto& opcode) {
|
||||
return opcode.GetId() == OpCode::Id::SHL_IMM &&
|
||||
instr.gpr0.Value() == ldc_tracked_register;
|
||||
},
|
||||
[](auto instr, const auto&) { return instr.gpr8.Value(); });
|
||||
}
|
||||
|
||||
std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos,
|
||||
u64 shl_tracked_register) {
|
||||
return TrackInstruction<u32>(state, pos,
|
||||
[shl_tracked_register](auto instr, const auto& opcode) {
|
||||
return opcode.GetId() == OpCode::Id::IMNMX_IMM &&
|
||||
instr.gpr0.Value() == shl_tracked_register;
|
||||
},
|
||||
[](auto instr, const auto&) {
|
||||
return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) {
|
||||
const auto brx_info = GetBRXInfo(state, pos);
|
||||
if (!brx_info) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
const auto [relative_position, brx_tracked_register] = *brx_info;
|
||||
|
||||
const auto ldc_info = TrackLDC(state, pos, brx_tracked_register);
|
||||
if (!ldc_info) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto [buffer_info, ldc_tracked_register] = *ldc_info;
|
||||
|
||||
const auto shl_tracked_register = TrackSHLRegister(state, pos, ldc_tracked_register);
|
||||
if (!shl_tracked_register) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto entries = TrackIMNMXValue(state, pos, *shl_tracked_register);
|
||||
if (!entries) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return BranchIndirectInfo{buffer_info.index, buffer_info.offset, *entries, relative_position};
|
||||
}
|
||||
|
||||
std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) {
|
||||
@@ -420,30 +428,30 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRX: {
|
||||
auto tmp = TrackBranchIndirectInfo(state, address, offset);
|
||||
if (tmp) {
|
||||
auto result = *tmp;
|
||||
std::vector<CaseBranch> branches{};
|
||||
s32 pc_target = offset + result.relative_position;
|
||||
for (u32 i = 0; i < result.entries; i++) {
|
||||
auto k = state.locker.ObtainKey(result.buffer, result.offset + i * 4);
|
||||
if (!k) {
|
||||
return {ParseResult::AbnormalFlow, parse_info};
|
||||
}
|
||||
u32 value = *k;
|
||||
u32 target = static_cast<u32>((value >> 3) + pc_target);
|
||||
insert_label(state, target);
|
||||
branches.emplace_back(value, target);
|
||||
}
|
||||
parse_info.end_address = offset;
|
||||
parse_info.branch_info = MakeBranchInfo<MultiBranch>(
|
||||
static_cast<u32>(instr.gpr8.Value()), std::move(branches));
|
||||
|
||||
return {ParseResult::ControlCaught, parse_info};
|
||||
} else {
|
||||
const auto tmp = TrackBranchIndirectInfo(state, offset);
|
||||
if (!tmp) {
|
||||
LOG_WARNING(HW_GPU, "BRX Track Unsuccesful");
|
||||
return {ParseResult::AbnormalFlow, parse_info};
|
||||
}
|
||||
return {ParseResult::AbnormalFlow, parse_info};
|
||||
|
||||
const auto result = *tmp;
|
||||
const s32 pc_target = offset + result.relative_position;
|
||||
std::vector<CaseBranch> branches;
|
||||
for (u32 i = 0; i < result.entries; i++) {
|
||||
auto key = state.locker.ObtainKey(result.buffer, result.offset + i * 4);
|
||||
if (!key) {
|
||||
return {ParseResult::AbnormalFlow, parse_info};
|
||||
}
|
||||
u32 value = *key;
|
||||
u32 target = static_cast<u32>((value >> 3) + pc_target);
|
||||
insert_label(state, target);
|
||||
branches.emplace_back(value, target);
|
||||
}
|
||||
parse_info.end_address = offset;
|
||||
parse_info.branch_info = MakeBranchInfo<MultiBranch>(
|
||||
static_cast<u32>(instr.gpr8.Value()), std::move(branches));
|
||||
|
||||
return {ParseResult::ControlCaught, parse_info};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -43,12 +43,12 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::FMUL_IMM: {
|
||||
// FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
|
||||
if (instr.fmul.tab5cb8_2 != 0) {
|
||||
LOG_WARNING(HW_GPU, "FMUL tab5cb8_2({}) is not implemented",
|
||||
instr.fmul.tab5cb8_2.Value());
|
||||
LOG_DEBUG(HW_GPU, "FMUL tab5cb8_2({}) is not implemented",
|
||||
instr.fmul.tab5cb8_2.Value());
|
||||
}
|
||||
if (instr.fmul.tab5c68_0 != 1) {
|
||||
LOG_WARNING(HW_GPU, "FMUL tab5cb8_0({}) is not implemented",
|
||||
instr.fmul.tab5c68_0.Value());
|
||||
LOG_DEBUG(HW_GPU, "FMUL tab5cb8_0({}) is not implemented",
|
||||
instr.fmul.tab5c68_0.Value());
|
||||
}
|
||||
|
||||
op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b);
|
||||
@@ -144,10 +144,11 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::RRO_C:
|
||||
case OpCode::Id::RRO_R:
|
||||
case OpCode::Id::RRO_IMM: {
|
||||
LOG_DEBUG(HW_GPU, "(STUBBED) RRO used");
|
||||
|
||||
// Currently RRO is only implemented as a register move.
|
||||
op_b = GetOperandAbsNegFloat(op_b, instr.alu.abs_b, instr.alu.negate_b);
|
||||
SetRegister(bb, instr.gpr0, op_b);
|
||||
LOG_WARNING(HW_GPU, "RRO instruction is incomplete");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -21,8 +21,8 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
|
||||
|
||||
if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
|
||||
opcode->get().GetId() == OpCode::Id::HADD2_R) {
|
||||
if (instr.alu_half.ftz != 0) {
|
||||
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
|
||||
if (instr.alu_half.ftz == 0) {
|
||||
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
|
||||
if (instr.alu_half_imm.ftz != 0) {
|
||||
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
|
||||
if (instr.alu_half_imm.ftz == 0) {
|
||||
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
|
||||
}
|
||||
} else {
|
||||
if (instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None) {
|
||||
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
|
||||
if (instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::FTZ) {
|
||||
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) {
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented");
|
||||
if (instr.ffma.tab5980_0 != 1) {
|
||||
LOG_WARNING(HW_GPU, "FFMA tab5980_0({}) not implemented", instr.ffma.tab5980_0.Value());
|
||||
LOG_DEBUG(HW_GPU, "FFMA tab5980_0({}) not implemented", instr.ffma.tab5980_0.Value());
|
||||
}
|
||||
if (instr.ffma.tab5980_1 != 0) {
|
||||
LOG_WARNING(HW_GPU, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value());
|
||||
LOG_DEBUG(HW_GPU, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value());
|
||||
}
|
||||
|
||||
const Node op_a = GetRegister(instr.gpr8);
|
||||
|
||||
@@ -20,8 +20,8 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
if (instr.hset2.ftz != 0) {
|
||||
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
|
||||
if (instr.hset2.ftz == 0) {
|
||||
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
|
||||
}
|
||||
|
||||
Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a);
|
||||
|
||||
@@ -19,7 +19,9 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
LOG_DEBUG(HW_GPU, "ftz={}", static_cast<u32>(instr.hsetp2.ftz));
|
||||
if (instr.hsetp2.ftz != 0) {
|
||||
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
|
||||
}
|
||||
|
||||
Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a);
|
||||
op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a);
|
||||
|
||||
@@ -143,39 +143,37 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
|
||||
const auto offset{static_cast<std::size_t>(image.index.Value())};
|
||||
if (const auto existing_image = TryUseExistingImage(offset, type)) {
|
||||
return *existing_image;
|
||||
const auto offset = static_cast<u32>(image.index.Value());
|
||||
|
||||
const auto it =
|
||||
std::find_if(std::begin(used_images), std::end(used_images),
|
||||
[offset](const Image& entry) { return entry.GetOffset() == offset; });
|
||||
if (it != std::end(used_images)) {
|
||||
ASSERT(!it->IsBindless() && it->GetType() == it->GetType());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const std::size_t next_index{used_images.size()};
|
||||
return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
|
||||
const auto next_index = static_cast<u32>(used_images.size());
|
||||
return used_images.emplace_back(next_index, offset, type);
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
|
||||
const Node image_register{GetRegister(reg)};
|
||||
const auto [base_image, cbuf_index, cbuf_offset]{
|
||||
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
|
||||
const Node image_register = GetRegister(reg);
|
||||
const auto [base_image, buffer, offset] =
|
||||
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()));
|
||||
|
||||
if (const auto image = TryUseExistingImage(cbuf_key, type)) {
|
||||
return *image;
|
||||
const auto it =
|
||||
std::find_if(std::begin(used_images), std::end(used_images),
|
||||
[buffer = buffer, offset = offset](const Image& entry) {
|
||||
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
||||
});
|
||||
if (it != std::end(used_images)) {
|
||||
ASSERT(it->IsBindless() && it->GetType() == it->GetType());
|
||||
return *it;
|
||||
}
|
||||
|
||||
const std::size_t next_index{used_images.size()};
|
||||
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
|
||||
auto it = used_images.find(offset);
|
||||
if (it == used_images.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& image = it->second;
|
||||
ASSERT(image.GetType() == type);
|
||||
|
||||
return ℑ
|
||||
const auto next_index = static_cast<u32>(used_images.size());
|
||||
return used_images.emplace_back(next_index, offset, buffer, type);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -44,10 +44,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
bool is_bindless = false;
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::TEX: {
|
||||
if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const TextureType texture_type{instr.tex.texture_type};
|
||||
const bool is_array = instr.tex.array != 0;
|
||||
const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
|
||||
@@ -62,10 +58,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
|
||||
if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const TextureType texture_type{instr.tex_b.texture_type};
|
||||
const bool is_array = instr.tex_b.array != 0;
|
||||
const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
|
||||
@@ -82,10 +74,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
const bool depth_compare = instr.texs.UsesMiscMode(TextureMiscMode::DC);
|
||||
const auto process_mode = instr.texs.GetTextureProcessMode();
|
||||
|
||||
if (instr.texs.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TEXS.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const Node4 components =
|
||||
GetTexsCode(instr, texture_type, process_mode, depth_compare, is_array);
|
||||
|
||||
@@ -107,10 +95,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP),
|
||||
"PTP is not implemented");
|
||||
|
||||
if (instr.tld4.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TLD4.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const auto texture_type = instr.tld4.texture_type.Value();
|
||||
const bool depth_compare = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::DC)
|
||||
: instr.tld4.UsesMiscMode(TextureMiscMode::DC);
|
||||
@@ -125,9 +109,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::TLD4S: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
if (instr.tld4s.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TLD4S.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const bool depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
|
||||
const Node op_a = GetRegister(instr.gpr8);
|
||||
@@ -164,10 +145,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
is_bindless = true;
|
||||
[[fallthrough]];
|
||||
case OpCode::Id::TXQ: {
|
||||
if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TXQ.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
// TODO: The new commits on the texture refactor, change the way samplers work.
|
||||
// Sadly, not all texture instructions specify the type of texture their sampler
|
||||
// uses. This must be fixed at a later instance.
|
||||
@@ -205,10 +182,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
|
||||
"NDV is not implemented");
|
||||
|
||||
if (instr.tmml.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TMML.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
auto texture_type = instr.tmml.texture_type.Value();
|
||||
const bool is_array = instr.tmml.array != 0;
|
||||
const auto& sampler =
|
||||
@@ -254,25 +227,17 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld.ms, "MS is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld.cl, "CL is not implemented");
|
||||
|
||||
if (instr.tld.nodep_flag) {
|
||||
LOG_WARNING(HW_GPU, "TLD.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
WriteTexInstructionFloat(bb, instr, GetTldCode(instr));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLDS: {
|
||||
const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
|
||||
const TextureType texture_type{instr.tlds.GetTextureType()};
|
||||
const bool is_array{instr.tlds.IsArrayTexture()};
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
|
||||
|
||||
if (instr.tlds.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TLDS.NODEP implementation is incomplete");
|
||||
}
|
||||
|
||||
const Node4 components = GetTldsCode(instr, texture_type, is_array);
|
||||
|
||||
if (instr.tlds.fp32_flag) {
|
||||
@@ -293,77 +258,80 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
|
||||
std::optional<SamplerInfo> sampler_info) {
|
||||
const auto offset = static_cast<u32>(sampler.index.Value());
|
||||
|
||||
Tegra::Shader::TextureType type;
|
||||
TextureType type;
|
||||
bool is_array;
|
||||
bool is_shadow;
|
||||
if (sampler_info) {
|
||||
type = sampler_info->type;
|
||||
is_array = sampler_info->is_array;
|
||||
is_shadow = sampler_info->is_shadow;
|
||||
} else if (auto sampler = locker.ObtainBoundSampler(offset); sampler) {
|
||||
} else if (const auto sampler = locker.ObtainBoundSampler(offset)) {
|
||||
type = sampler->texture_type.Value();
|
||||
is_array = sampler->is_array.Value() != 0;
|
||||
is_shadow = sampler->is_shadow.Value() != 0;
|
||||
} else {
|
||||
type = Tegra::Shader::TextureType::Texture2D;
|
||||
LOG_WARNING(HW_GPU, "Unknown sampler info");
|
||||
type = TextureType::Texture2D;
|
||||
is_array = false;
|
||||
is_shadow = false;
|
||||
}
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto itr =
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[&](const Sampler& entry) { return entry.GetOffset() == offset; });
|
||||
if (itr != used_samplers.end()) {
|
||||
ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
|
||||
itr->IsShadow() == is_shadow);
|
||||
return *itr;
|
||||
[offset](const Sampler& entry) { return entry.GetOffset() == offset; });
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(!it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
|
||||
it->IsShadow() == is_shadow);
|
||||
return *it;
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const std::size_t next_index = used_samplers.size();
|
||||
const Sampler entry{offset, next_index, type, is_array, is_shadow};
|
||||
return *used_samplers.emplace(entry).first;
|
||||
} // namespace VideoCommon::Shader
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return used_samplers.emplace_back(Sampler(next_index, offset, type, is_array, is_shadow));
|
||||
}
|
||||
|
||||
const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
|
||||
std::optional<SamplerInfo> sampler_info) {
|
||||
const Node sampler_register = GetRegister(reg);
|
||||
const auto [base_sampler, cbuf_index, cbuf_offset] =
|
||||
const auto [base_sampler, buffer, offset] =
|
||||
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||
ASSERT(base_sampler != nullptr);
|
||||
const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset);
|
||||
Tegra::Shader::TextureType type;
|
||||
|
||||
TextureType type;
|
||||
bool is_array;
|
||||
bool is_shadow;
|
||||
if (sampler_info) {
|
||||
type = sampler_info->type;
|
||||
is_array = sampler_info->is_array;
|
||||
is_shadow = sampler_info->is_shadow;
|
||||
} else if (auto sampler = locker.ObtainBindlessSampler(cbuf_index, cbuf_offset); sampler) {
|
||||
} else if (const auto sampler = locker.ObtainBindlessSampler(buffer, offset)) {
|
||||
type = sampler->texture_type.Value();
|
||||
is_array = sampler->is_array.Value() != 0;
|
||||
is_shadow = sampler->is_shadow.Value() != 0;
|
||||
} else {
|
||||
type = Tegra::Shader::TextureType::Texture2D;
|
||||
LOG_WARNING(HW_GPU, "Unknown sampler info");
|
||||
type = TextureType::Texture2D;
|
||||
is_array = false;
|
||||
is_shadow = false;
|
||||
}
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto itr =
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[&](const Sampler& entry) { return entry.GetOffset() == cbuf_key; });
|
||||
if (itr != used_samplers.end()) {
|
||||
ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
|
||||
itr->IsShadow() == is_shadow);
|
||||
return *itr;
|
||||
[buffer = buffer, offset = offset](const Sampler& entry) {
|
||||
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
||||
});
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
|
||||
it->IsShadow() == is_shadow);
|
||||
return *it;
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const std::size_t next_index = used_samplers.size();
|
||||
const Sampler entry{cbuf_index, cbuf_offset, next_index, type, is_array, is_shadow};
|
||||
return *used_samplers.emplace(entry).first;
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return used_samplers.emplace_back(
|
||||
Sampler(next_index, offset, buffer, type, is_array, is_shadow));
|
||||
}
|
||||
|
||||
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
|
||||
|
||||
@@ -230,62 +230,49 @@ using NodeBlock = std::vector<Node>;
|
||||
class Sampler {
|
||||
public:
|
||||
/// This constructor is for bound samplers
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{false} {}
|
||||
constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow)
|
||||
: index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow} {}
|
||||
|
||||
/// This constructor is for bindless samplers
|
||||
explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
|
||||
Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
|
||||
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
|
||||
is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {}
|
||||
constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow)
|
||||
: index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
|
||||
is_shadow{is_shadow}, is_bindless{true} {}
|
||||
|
||||
/// This constructor is for serialization/deserialization
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow, bool is_bindless)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{is_bindless} {}
|
||||
|
||||
std::size_t GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t GetIndex() const {
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
Tegra::Shader::TextureType GetType() const {
|
||||
constexpr u32 GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
constexpr u32 GetBuffer() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
constexpr Tegra::Shader::TextureType GetType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
bool IsArray() const {
|
||||
constexpr bool IsArray() const {
|
||||
return is_array;
|
||||
}
|
||||
|
||||
bool IsShadow() const {
|
||||
constexpr bool IsShadow() const {
|
||||
return is_shadow;
|
||||
}
|
||||
|
||||
bool IsBindless() const {
|
||||
constexpr bool IsBindless() const {
|
||||
return is_bindless;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
|
||||
}
|
||||
|
||||
bool operator<(const Sampler& rhs) const {
|
||||
return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
|
||||
std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
|
||||
rhs.is_bindless);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
|
||||
/// instruction.
|
||||
std::size_t offset{};
|
||||
std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
|
||||
u32 index{}; ///< Emulated index given for the this sampler.
|
||||
u32 offset{}; ///< Offset in the const buffer from where the sampler is being read.
|
||||
u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers).
|
||||
|
||||
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
||||
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
||||
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
||||
@@ -294,18 +281,13 @@ private:
|
||||
|
||||
class Image final {
|
||||
public:
|
||||
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
|
||||
: offset{offset}, index{index}, type{type}, is_bindless{false} {}
|
||||
/// This constructor is for bound images
|
||||
constexpr explicit Image(u32 index, u32 offset, Tegra::Shader::ImageType type)
|
||||
: index{index}, offset{offset}, type{type} {}
|
||||
|
||||
constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
|
||||
Tegra::Shader::ImageType type)
|
||||
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
|
||||
is_bindless{true} {}
|
||||
|
||||
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
|
||||
bool is_bindless, bool is_written, bool is_read, bool is_atomic)
|
||||
: offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
|
||||
is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
|
||||
/// This constructor is for bindless samplers
|
||||
constexpr explicit Image(u32 index, u32 offset, u32 buffer, Tegra::Shader::ImageType type)
|
||||
: index{index}, offset{offset}, buffer{buffer}, type{type}, is_bindless{true} {}
|
||||
|
||||
void MarkWrite() {
|
||||
is_written = true;
|
||||
@@ -321,12 +303,16 @@ public:
|
||||
is_atomic = true;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetOffset() const {
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
constexpr u32 GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetIndex() const {
|
||||
return index;
|
||||
constexpr u32 GetBuffer() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
constexpr Tegra::Shader::ImageType GetType() const {
|
||||
@@ -349,18 +335,11 @@ public:
|
||||
return is_atomic;
|
||||
}
|
||||
|
||||
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
|
||||
}
|
||||
|
||||
constexpr bool operator<(const Image& rhs) const {
|
||||
return std::tie(offset, index, type, is_bindless) <
|
||||
std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
|
||||
}
|
||||
|
||||
private:
|
||||
u64 offset{};
|
||||
std::size_t index{};
|
||||
u32 index{};
|
||||
u32 offset{};
|
||||
u32 buffer{};
|
||||
|
||||
Tegra::Shader::ImageType type{};
|
||||
bool is_bindless{};
|
||||
bool is_written{};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
@@ -95,11 +96,11 @@ public:
|
||||
return used_cbufs;
|
||||
}
|
||||
|
||||
const std::set<Sampler>& GetSamplers() const {
|
||||
const std::list<Sampler>& GetSamplers() const {
|
||||
return used_samplers;
|
||||
}
|
||||
|
||||
const std::map<u64, Image>& GetImages() const {
|
||||
const std::list<Image>& GetImages() const {
|
||||
return used_images;
|
||||
}
|
||||
|
||||
@@ -316,9 +317,6 @@ private:
|
||||
/// Access a bindless image sampler.
|
||||
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
|
||||
|
||||
/// Tries to access an existing image, updating it's state as needed
|
||||
Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
|
||||
|
||||
/// Extracts a sequence of bits from a node
|
||||
Node BitfieldExtract(Node value, u32 offset, u32 bits);
|
||||
|
||||
@@ -402,8 +400,8 @@ private:
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
|
||||
std::map<u32, ConstBuffer> used_cbufs;
|
||||
std::set<Sampler> used_samplers;
|
||||
std::map<u64, Image> used_images;
|
||||
std::list<Sampler> used_samplers;
|
||||
std::list<Image> used_images;
|
||||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
|
||||
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
|
||||
bool uses_layer{};
|
||||
|
||||
@@ -132,6 +132,8 @@ enum class SwizzleSource : u32 {
|
||||
};
|
||||
|
||||
union TextureHandle {
|
||||
TextureHandle(u32 raw) : raw{raw} {}
|
||||
|
||||
u32 raw;
|
||||
BitField<0, 20, u32> tic_id;
|
||||
BitField<20, 12, u32> tsc_id;
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <LUrlParser.h>
|
||||
#include <fmt/format.h>
|
||||
#include <httplib.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -16,10 +18,10 @@ namespace WebService {
|
||||
|
||||
constexpr std::array<const char, 1> API_VERSION{'1'};
|
||||
|
||||
constexpr u32 HTTP_PORT = 80;
|
||||
constexpr u32 HTTPS_PORT = 443;
|
||||
constexpr int HTTP_PORT = 80;
|
||||
constexpr int HTTPS_PORT = 443;
|
||||
|
||||
constexpr u32 TIMEOUT_SECONDS = 30;
|
||||
constexpr std::size_t TIMEOUT_SECONDS = 30;
|
||||
|
||||
struct Client::Impl {
|
||||
Impl(std::string host, std::string username, std::string token)
|
||||
@@ -31,8 +33,9 @@ struct Client::Impl {
|
||||
}
|
||||
|
||||
/// A generic function handles POST, GET and DELETE request together
|
||||
Common::WebResult GenericJson(const std::string& method, const std::string& path,
|
||||
const std::string& data, bool allow_anonymous) {
|
||||
Common::WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, bool allow_anonymous,
|
||||
const std::string& accept) {
|
||||
if (jwt.empty()) {
|
||||
UpdateJWT();
|
||||
}
|
||||
@@ -43,11 +46,11 @@ struct Client::Impl {
|
||||
"Credentials needed"};
|
||||
}
|
||||
|
||||
auto result = GenericJson(method, path, data, jwt);
|
||||
auto result = GenericRequest(method, path, data, accept, jwt);
|
||||
if (result.result_string == "401") {
|
||||
// Try again with new JWT
|
||||
UpdateJWT();
|
||||
result = GenericJson(method, path, data, jwt);
|
||||
result = GenericRequest(method, path, data, accept, jwt);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -56,12 +59,13 @@ struct Client::Impl {
|
||||
/**
|
||||
* A generic function with explicit authentication method specified
|
||||
* JWT is used if the jwt parameter is not empty
|
||||
* username + token is used if jwt is empty but username and token are not empty
|
||||
* anonymous if all of jwt, username and token are empty
|
||||
* username + token is used if jwt is empty but username and token are
|
||||
* not empty anonymous if all of jwt, username and token are empty
|
||||
*/
|
||||
Common::WebResult GenericJson(const std::string& method, const std::string& path,
|
||||
const std::string& data, const std::string& jwt = "",
|
||||
const std::string& username = "", const std::string& token = "") {
|
||||
Common::WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, const std::string& accept,
|
||||
const std::string& jwt = "", const std::string& username = "",
|
||||
const std::string& token = "") {
|
||||
if (cli == nullptr) {
|
||||
auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
|
||||
int port;
|
||||
@@ -132,8 +136,7 @@ struct Client::Impl {
|
||||
return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
|
||||
}
|
||||
|
||||
if (content_type->second.find("application/json") == std::string::npos &&
|
||||
content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
|
||||
if (content_type->second.find(accept) == std::string::npos) {
|
||||
LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
|
||||
content_type->second);
|
||||
return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
|
||||
@@ -147,7 +150,7 @@ struct Client::Impl {
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
|
||||
auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
|
||||
if (result.result_code != Common::WebResult::Code::Success) {
|
||||
LOG_ERROR(WebService, "UpdateJWT failed");
|
||||
} else {
|
||||
@@ -180,16 +183,29 @@ Client::~Client() = default;
|
||||
|
||||
Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous) {
|
||||
return impl->GenericJson("POST", path, data, allow_anonymous);
|
||||
return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericJson("GET", path, "", allow_anonymous);
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous) {
|
||||
return impl->GenericJson("DELETE", path, data, allow_anonymous);
|
||||
return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetExternalJWT(const std::string& audience) {
|
||||
return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
|
||||
"text/html");
|
||||
}
|
||||
|
||||
} // namespace WebService
|
||||
|
||||
@@ -46,6 +46,29 @@ public:
|
||||
Common::WebResult DeleteJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Gets a plain string from the specified path.
|
||||
* @param path the URL segment after the host address.
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetPlain(const std::string& path, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Gets an PNG image from the specified path.
|
||||
* @param path the URL segment after the host address.
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetImage(const std::string& path, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Requests an external JWT for the specific audience provided.
|
||||
* @param audience the audience of the JWT requested.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetExternalJWT(const std::string& audience);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -11,6 +11,31 @@
|
||||
#include "yuzu/configuration/configure_web.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
static constexpr char token_delimiter{':'};
|
||||
|
||||
static std::string GenerateDisplayToken(const std::string& username, const std::string& token) {
|
||||
if (username.empty() || token.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string unencoded_display_token{username + token_delimiter + token};
|
||||
QByteArray b{unencoded_display_token.c_str()};
|
||||
QByteArray b64 = b.toBase64();
|
||||
return b64.toStdString();
|
||||
}
|
||||
|
||||
static std::string UsernameFromDisplayToken(const std::string& display_token) {
|
||||
const std::string unencoded_display_token{
|
||||
QByteArray::fromBase64(display_token.c_str()).toStdString()};
|
||||
return unencoded_display_token.substr(0, unencoded_display_token.find(token_delimiter));
|
||||
}
|
||||
|
||||
static std::string TokenFromDisplayToken(const std::string& display_token) {
|
||||
const std::string unencoded_display_token{
|
||||
QByteArray::fromBase64(display_token.c_str()).toStdString()};
|
||||
return unencoded_display_token.substr(unencoded_display_token.find(token_delimiter) + 1);
|
||||
}
|
||||
|
||||
ConfigureWeb::ConfigureWeb(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
|
||||
ui->setupUi(this);
|
||||
@@ -63,13 +88,18 @@ void ConfigureWeb::SetConfiguration() {
|
||||
ui->web_signup_link->setOpenExternalLinks(true);
|
||||
ui->web_token_info_link->setOpenExternalLinks(true);
|
||||
|
||||
if (Settings::values.yuzu_username.empty()) {
|
||||
ui->username->setText(tr("Unspecified"));
|
||||
} else {
|
||||
ui->username->setText(QString::fromStdString(Settings::values.yuzu_username));
|
||||
}
|
||||
|
||||
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
|
||||
ui->edit_username->setText(QString::fromStdString(Settings::values.yuzu_username));
|
||||
ui->edit_token->setText(QString::fromStdString(Settings::values.yuzu_token));
|
||||
ui->edit_token->setText(QString::fromStdString(
|
||||
GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token)));
|
||||
|
||||
// Connect after setting the values, to avoid calling OnLoginChanged now
|
||||
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
|
||||
user_verified = true;
|
||||
|
||||
@@ -80,12 +110,13 @@ void ConfigureWeb::ApplyConfiguration() {
|
||||
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
|
||||
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
|
||||
if (user_verified) {
|
||||
Settings::values.yuzu_username = ui->edit_username->text().toStdString();
|
||||
Settings::values.yuzu_token = ui->edit_token->text().toStdString();
|
||||
Settings::values.yuzu_username =
|
||||
UsernameFromDisplayToken(ui->edit_token->text().toStdString());
|
||||
Settings::values.yuzu_token = TokenFromDisplayToken(ui->edit_token->text().toStdString());
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Username and token not verified"),
|
||||
tr("Username and token were not verified. The changes to your "
|
||||
"username and/or token have not been saved."));
|
||||
QMessageBox::warning(
|
||||
this, tr("Token not verified"),
|
||||
tr("Token was not verified. The change to your token has not been saved."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,17 +127,15 @@ void ConfigureWeb::RefreshTelemetryID() {
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginChanged() {
|
||||
if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
|
||||
if (ui->edit_token->text().isEmpty()) {
|
||||
user_verified = true;
|
||||
|
||||
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
|
||||
ui->label_username_verified->setPixmap(pixmap);
|
||||
ui->label_token_verified->setPixmap(pixmap);
|
||||
} else {
|
||||
user_verified = false;
|
||||
|
||||
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
|
||||
ui->label_username_verified->setPixmap(pixmap);
|
||||
ui->label_token_verified->setPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
@@ -114,10 +143,11 @@ void ConfigureWeb::OnLoginChanged() {
|
||||
void ConfigureWeb::VerifyLogin() {
|
||||
ui->button_verify_login->setDisabled(true);
|
||||
ui->button_verify_login->setText(tr("Verifying..."));
|
||||
verify_watcher.setFuture(QtConcurrent::run([username = ui->edit_username->text().toStdString(),
|
||||
token = ui->edit_token->text().toStdString()] {
|
||||
return Core::VerifyLogin(username, token);
|
||||
}));
|
||||
verify_watcher.setFuture(QtConcurrent::run(
|
||||
[username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
|
||||
token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
|
||||
return Core::VerifyLogin(username, token);
|
||||
}));
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginVerified() {
|
||||
@@ -127,16 +157,15 @@ void ConfigureWeb::OnLoginVerified() {
|
||||
user_verified = true;
|
||||
|
||||
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
|
||||
ui->label_username_verified->setPixmap(pixmap);
|
||||
ui->label_token_verified->setPixmap(pixmap);
|
||||
ui->username->setText(
|
||||
QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
|
||||
} else {
|
||||
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
|
||||
ui->label_username_verified->setPixmap(pixmap);
|
||||
ui->label_token_verified->setPixmap(pixmap);
|
||||
|
||||
QMessageBox::critical(
|
||||
this, tr("Verification failed"),
|
||||
tr("Verification failed. Check that you have entered your username and token "
|
||||
"correctly, and that your internet connection is working."));
|
||||
ui->username->setText(tr("Unspecified"));
|
||||
QMessageBox::critical(this, tr("Verification failed"),
|
||||
tr("Verification failed. Check that you have entered your token "
|
||||
"correctly, and that your internet connection is working."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_username">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="username" />
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_token">
|
||||
@@ -79,14 +75,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_username_verified">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_token">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
<number>80</number>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
|
||||
@@ -172,17 +172,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() con
|
||||
return list;
|
||||
}
|
||||
|
||||
QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
|
||||
switch (reset_type) {
|
||||
case Kernel::ResetType::Automatic:
|
||||
return tr("automatic reset");
|
||||
case Kernel::ResetType::Manual:
|
||||
return tr("manual reset");
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
WaitTreeObjectList::WaitTreeObjectList(
|
||||
const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list, bool w_all)
|
||||
: object_list(list), wait_all(w_all) {}
|
||||
@@ -336,16 +325,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeWaitObject(object) {}
|
||||
WaitTreeEvent::~WaitTreeEvent() = default;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(
|
||||
tr("reset type = %1")
|
||||
.arg(GetResetTypeQString(
|
||||
static_cast<const Kernel::ReadableEvent&>(object).GetResetType()))));
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
|
||||
: thread_list(list) {}
|
||||
WaitTreeThreadList::~WaitTreeThreadList() = default;
|
||||
|
||||
@@ -111,8 +111,6 @@ public:
|
||||
|
||||
protected:
|
||||
const Kernel::WaitObject& object;
|
||||
|
||||
static QString GetResetTypeQString(Kernel::ResetType reset_type);
|
||||
};
|
||||
|
||||
class WaitTreeObjectList : public WaitTreeExpandableItem {
|
||||
@@ -146,8 +144,6 @@ class WaitTreeEvent : public WaitTreeWaitObject {
|
||||
public:
|
||||
explicit WaitTreeEvent(const Kernel::ReadableEvent& object);
|
||||
~WaitTreeEvent() override;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
};
|
||||
|
||||
class WaitTreeThreadList : public WaitTreeExpandableItem {
|
||||
|
||||
Reference in New Issue
Block a user