Compare commits
28 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fadab1d5f3 | ||
|
|
8262aeeac8 | ||
|
|
ff2f0d980a | ||
|
|
0c8b7c00e8 | ||
|
|
f362cf46ee | ||
|
|
0197e28cc9 | ||
|
|
81a0082f6b | ||
|
|
225ff1130f | ||
|
|
b3962e7d1e | ||
|
|
3abba08080 | ||
|
|
e8bbafb746 | ||
|
|
40d2dcabd7 | ||
|
|
f41eb95e13 | ||
|
|
08b8fcbe6d | ||
|
|
316327f487 | ||
|
|
c7ce472eeb | ||
|
|
869075867b | ||
|
|
da32c648bf | ||
|
|
a71346cd7c | ||
|
|
6c464a2a4a | ||
|
|
49d92aa661 | ||
|
|
faa431b274 | ||
|
|
f87ea8fa8b | ||
|
|
0c01c34eff | ||
|
|
e73927cfc2 | ||
|
|
c691fa4074 | ||
|
|
f2dcb39049 | ||
|
|
a7b5ab4d9a |
@@ -809,16 +809,16 @@ IOFile::~IOFile() {
|
||||
Close();
|
||||
}
|
||||
|
||||
IOFile::IOFile(IOFile&& other) {
|
||||
IOFile::IOFile(IOFile&& other) noexcept {
|
||||
Swap(other);
|
||||
}
|
||||
|
||||
IOFile& IOFile::operator=(IOFile&& other) {
|
||||
IOFile& IOFile::operator=(IOFile&& other) noexcept {
|
||||
Swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void IOFile::Swap(IOFile& other) {
|
||||
void IOFile::Swap(IOFile& other) noexcept {
|
||||
std::swap(m_file, other.m_file);
|
||||
std::swap(m_good, other.m_good);
|
||||
}
|
||||
|
||||
@@ -160,10 +160,10 @@ public:
|
||||
|
||||
~IOFile();
|
||||
|
||||
IOFile(IOFile&& other);
|
||||
IOFile& operator=(IOFile&& other);
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
|
||||
void Swap(IOFile& other);
|
||||
void Swap(IOFile& other) noexcept;
|
||||
|
||||
bool Open(const std::string& filename, const char openmode[]);
|
||||
bool Close();
|
||||
@@ -202,11 +202,15 @@ public:
|
||||
return items_written;
|
||||
}
|
||||
|
||||
size_t ReadBytes(void* data, size_t length) {
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
size_t WriteBytes(const void* data, size_t length) {
|
||||
template <typename T>
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,8 +167,7 @@ std::string MemUsage() {
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = Common::StringFromFormat(
|
||||
"%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
|
||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
|
||||
@@ -46,76 +46,6 @@ bool AsciiToHex(const char* _szValue, u32& result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) {
|
||||
int writtenCount;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// You would think *printf are simple, right? Iterate on each character,
|
||||
// if it's a format specifier handle it properly, etc.
|
||||
//
|
||||
// Nooooo. Not according to the C standard.
|
||||
//
|
||||
// According to the C99 standard (7.19.6.1 "The fprintf function")
|
||||
// The format shall be a multibyte character sequence
|
||||
//
|
||||
// Because some character encodings might have '%' signs in the middle of
|
||||
// a multibyte sequence (SJIS for example only specifies that the first
|
||||
// byte of a 2 byte sequence is "high", the second byte can be anything),
|
||||
// printf functions have to decode the multibyte sequences and try their
|
||||
// best to not screw up.
|
||||
//
|
||||
// Unfortunately, on Windows, the locale for most languages is not UTF-8
|
||||
// as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the
|
||||
// locale, and completely fails when trying to decode UTF-8 as EUC-CN.
|
||||
//
|
||||
// On the other hand, the fix is simple: because we use UTF-8, no such
|
||||
// multibyte handling is required as we can simply assume that no '%' char
|
||||
// will be present in the middle of a multibyte sequence.
|
||||
//
|
||||
// This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
|
||||
static locale_t c_locale = nullptr;
|
||||
if (!c_locale)
|
||||
c_locale = _create_locale(LC_ALL, ".1252");
|
||||
writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
|
||||
#else
|
||||
writtenCount = vsnprintf(out, outsize, format, args);
|
||||
#endif
|
||||
|
||||
if (writtenCount > 0 && writtenCount < outsize) {
|
||||
out[writtenCount] = '\0';
|
||||
return true;
|
||||
} else {
|
||||
out[outsize - 1] = '\0';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string StringFromFormat(const char* format, ...) {
|
||||
va_list args;
|
||||
char* buf = nullptr;
|
||||
#ifdef _WIN32
|
||||
int required = 0;
|
||||
|
||||
va_start(args, format);
|
||||
required = _vscprintf(format, args);
|
||||
buf = new char[required + 1];
|
||||
CharArrayFromFormatV(buf, required + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
std::string temp = buf;
|
||||
delete[] buf;
|
||||
#else
|
||||
va_start(args, format);
|
||||
if (vasprintf(&buf, format, args) < 0)
|
||||
NGLOG_ERROR(Common, "Unable to allocate memory for string");
|
||||
va_end(args);
|
||||
|
||||
std::string temp = buf;
|
||||
free(buf);
|
||||
#endif
|
||||
return temp;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
|
||||
std::ostringstream oss;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -20,19 +19,6 @@ std::string ToLower(std::string str);
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(std::string str);
|
||||
|
||||
std::string StringFromFormat(const char* format, ...);
|
||||
// Cheap!
|
||||
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args);
|
||||
|
||||
template <size_t Count>
|
||||
inline void CharArrayFromFormat(char (&out)[Count], const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
CharArrayFromFormatV(out, Count, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Good
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <time.h>
|
||||
#include <ctime>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// windows.h needs to be included before other windows headers
|
||||
@@ -104,8 +107,8 @@ std::string Timer::GetTimeElapsedFormatted() const {
|
||||
// Hours
|
||||
u32 Hours = Minutes / 60;
|
||||
|
||||
std::string TmpStr = StringFromFormat("%02i:%02i:%02i:%03i", Hours, Minutes % 60, Seconds % 60,
|
||||
Milliseconds % 1000);
|
||||
std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours, Minutes % 60, Seconds % 60,
|
||||
Milliseconds % 1000);
|
||||
return TmpStr;
|
||||
}
|
||||
|
||||
@@ -165,11 +168,11 @@ std::string Timer::GetTimeFormatted() {
|
||||
#ifdef _WIN32
|
||||
struct timeb tp;
|
||||
(void)::ftime(&tp);
|
||||
return StringFromFormat("%s:%03i", tmp, tp.millitm);
|
||||
return fmt::format("{}:{:03}", tmp, tp.millitm);
|
||||
#else
|
||||
struct timeval t;
|
||||
(void)gettimeofday(&t, nullptr);
|
||||
return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
|
||||
return fmt::format("{}:{:03}", tmp, static_cast<int>(t.tv_usec / 1000));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
|
||||
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
|
||||
// The exact value used is of course unverified.
|
||||
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
|
||||
@@ -117,8 +119,6 @@ inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
namespace CoreTiming {
|
||||
|
||||
/**
|
||||
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
||||
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
|
||||
|
||||
@@ -167,6 +167,7 @@ struct DomainMessageHeader {
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 8, CommandType> command;
|
||||
BitField<8, 8, u32_le> input_object_count;
|
||||
BitField<16, 16, u32_le> size;
|
||||
};
|
||||
u32_le object_id;
|
||||
|
||||
@@ -298,6 +298,13 @@ public:
|
||||
|
||||
template <typename T>
|
||||
Kernel::SharedPtr<T> GetCopyObject(size_t index);
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->Session()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader()->input_object_count > 0);
|
||||
return context->GetDomainRequestHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
};
|
||||
|
||||
/// Pop ///
|
||||
|
||||
@@ -251,24 +251,26 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<u8> HLERequestContext::ReadBuffer() const {
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
|
||||
std::vector<u8> buffer;
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a) {
|
||||
buffer.resize(BufferDescriptorA()[0].Size());
|
||||
Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size());
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
} else {
|
||||
buffer.resize(BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
|
||||
const size_t buffer_size{GetWriteBufferSize()};
|
||||
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
|
||||
const size_t buffer_size{GetWriteBufferSize(buffer_index)};
|
||||
if (size > buffer_size) {
|
||||
NGLOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
@@ -276,26 +278,28 @@ size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
|
||||
}
|
||||
|
||||
if (is_buffer_b) {
|
||||
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
|
||||
Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
} else {
|
||||
Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size);
|
||||
Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const {
|
||||
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer, int buffer_index) const {
|
||||
return WriteBuffer(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
size_t HLERequestContext::GetReadBufferSize() const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
|
||||
return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size();
|
||||
size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
return is_buffer_a ? BufferDescriptorA()[buffer_index].Size()
|
||||
: BufferDescriptorX()[buffer_index].Size();
|
||||
}
|
||||
|
||||
size_t HLERequestContext::GetWriteBufferSize() const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
|
||||
return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size();
|
||||
size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
|
||||
return is_buffer_b ? BufferDescriptorB()[buffer_index].Size()
|
||||
: BufferDescriptorC()[buffer_index].Size();
|
||||
}
|
||||
|
||||
std::string HLERequestContext::Description() const {
|
||||
|
||||
@@ -164,19 +164,19 @@ public:
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
std::vector<u8> ReadBuffer() const;
|
||||
std::vector<u8> ReadBuffer(int buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
size_t WriteBuffer(const void* buffer, size_t size) const;
|
||||
size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
size_t WriteBuffer(const std::vector<u8>& buffer) const;
|
||||
size_t WriteBuffer(const std::vector<u8>& buffer, int buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get the size of the input buffer
|
||||
size_t GetReadBufferSize() const;
|
||||
size_t GetReadBufferSize(int buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get the size of the output buffer
|
||||
size_t GetWriteBufferSize() const;
|
||||
size_t GetWriteBufferSize(int buffer_index = 0) const;
|
||||
|
||||
template <typename T>
|
||||
SharedPtr<T> GetCopyObject(size_t index) {
|
||||
@@ -202,6 +202,16 @@ public:
|
||||
domain_objects.emplace_back(std::move(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetDomainRequestHandler(size_t index) const {
|
||||
return std::static_pointer_cast<T>(domain_request_handlers[index]);
|
||||
}
|
||||
|
||||
void SetDomainRequestHandlers(
|
||||
const std::vector<std::shared_ptr<SessionRequestHandler>>& handlers) {
|
||||
domain_request_handlers = handlers;
|
||||
}
|
||||
|
||||
/// Clears the list of objects so that no lingering objects are written accidentally to the
|
||||
/// response buffer.
|
||||
void ClearIncomingObjects() {
|
||||
@@ -245,6 +255,8 @@ private:
|
||||
unsigned data_payload_offset{};
|
||||
unsigned buffer_c_offset{};
|
||||
u32_le command{};
|
||||
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -61,6 +61,9 @@ void ServerSession::Acquire(Thread* thread) {
|
||||
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
||||
auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
if (domain_message_header) {
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
context.SetDomainRequestHandlers(domain_request_handlers);
|
||||
|
||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||
const u32 object_id{context.GetDomainMessageHeader()->object_id};
|
||||
switch (domain_message_header->command) {
|
||||
|
||||
@@ -510,7 +510,7 @@ static void ExitProcess() {
|
||||
/// Creates a new thread
|
||||
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
|
||||
u32 priority, s32 processor_id) {
|
||||
std::string name = Common::StringFromFormat("unknown-%llx", entry_point);
|
||||
std::string name = fmt::format("unknown-{:X}", entry_point);
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
|
||||
@@ -146,7 +146,8 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
if (nanoseconds == -1)
|
||||
return;
|
||||
|
||||
CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle);
|
||||
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
|
||||
callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
|
||||
@@ -57,7 +57,8 @@ void Timer::Set(s64 initial, s64 interval) {
|
||||
// Immediately invoke the callback
|
||||
Signal(0);
|
||||
} else {
|
||||
CoreTiming::ScheduleEvent(nsToCycles(initial), timer_callback_event_type, callback_handle);
|
||||
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), timer_callback_event_type,
|
||||
callback_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ void Timer::Signal(int cycles_late) {
|
||||
|
||||
if (interval_delay != 0) {
|
||||
// Reschedule the timer with the interval delay
|
||||
CoreTiming::ScheduleEvent(nsToCycles(interval_delay) - cycles_late,
|
||||
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late,
|
||||
timer_callback_event_type, callback_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr u32 sample_rate{48000};
|
||||
/// to more audio channels (probably when Docked I guess)
|
||||
constexpr u32 audio_channels{2};
|
||||
/// TODO(st4rk): find a proper value for the audio_ticks
|
||||
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)};
|
||||
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)};
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace Service::Audio {
|
||||
|
||||
/// TODO(bunnei): Find a proper value for the audio_ticks
|
||||
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)};
|
||||
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(shinyquagsire23): These need better values.
|
||||
constexpr u64 pad_update_ticks = BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
|
||||
@@ -37,7 +37,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
|
||||
{2, &PL_U::GetSize, "GetSize"},
|
||||
{3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
|
||||
{4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
|
||||
{5, nullptr, "GetSharedFontInOrderOfPriority"},
|
||||
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
@@ -116,4 +116,29 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(shared_font_mem);
|
||||
}
|
||||
|
||||
void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
|
||||
NGLOG_DEBUG(Service_NS, "called, language_code=%lx", language_code);
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
std::vector<u32> font_codes;
|
||||
std::vector<u32> font_offsets;
|
||||
std::vector<u32> font_sizes;
|
||||
|
||||
// TODO(ogniK): Have actual priority order
|
||||
for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) {
|
||||
font_codes.push_back(static_cast<u32>(i));
|
||||
font_offsets.push_back(SHARED_FONT_REGIONS[i].offset);
|
||||
font_sizes.push_back(SHARED_FONT_REGIONS[i].size);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(font_codes.data(), font_codes.size(), 0);
|
||||
ctx.WriteBuffer(font_offsets.data(), font_offsets.size(), 1);
|
||||
ctx.WriteBuffer(font_sizes.data(), font_sizes.size(), 2);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded
|
||||
rb.Push<u32>(static_cast<u32>(font_codes.size()));
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -21,6 +21,7 @@ private:
|
||||
void GetSize(Kernel::HLERequestContext& ctx);
|
||||
void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx);
|
||||
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
|
||||
void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/// Handle to shared memory region designated for a shared font
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
|
||||
|
||||
@@ -39,7 +39,6 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
IoctlInitalizeEx params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size={:#X}", params.big_page_size);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -135,7 +134,6 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
|
||||
channel = params.fd;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -58,7 +57,6 @@ u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& out
|
||||
IoctlClientData params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
user_data = params.data;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -91,7 +89,6 @@ u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>&
|
||||
u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
std::memcpy(&channel_priority, input.data(), input.size());
|
||||
NGLOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
|
||||
std::memcpy(output.data(), &channel_priority, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Add the different displays to the list of displays.
|
||||
|
||||
@@ -58,10 +58,9 @@ static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
// Number of params == bits 0-5 + bits 6-11
|
||||
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
|
||||
|
||||
std::string function_string =
|
||||
Common::StringFromFormat("function '%s': port=%s", name, port_name);
|
||||
std::string function_string = fmt::format("function '{}': port={}", name, port_name);
|
||||
for (int i = 1; i <= num_params; ++i) {
|
||||
function_string += Common::StringFromFormat(", cmd_buff[%i]=0x%X", i, cmd_buff[i]);
|
||||
function_string += fmt::format(", cmd_buff[{}]={:#X}", i, cmd_buff[i]);
|
||||
}
|
||||
return function_string;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public:
|
||||
private:
|
||||
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_Time, "called");
|
||||
SteadyClockTimePoint steady_clock_time_point{cyclesToMs(CoreTiming::GetTicks()) / 1000};
|
||||
SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(steady_clock_time_point);
|
||||
|
||||
@@ -56,13 +56,14 @@ static void UpdateTimeCallback(u64 userdata, int cycles_late) {
|
||||
|
||||
date_time.date_time = GetSystemTime();
|
||||
date_time.update_tick = CoreTiming::GetTicks();
|
||||
date_time.tick_to_second_coefficient = BASE_CLOCK_RATE;
|
||||
date_time.tick_to_second_coefficient = CoreTiming::BASE_CLOCK_RATE;
|
||||
date_time.tick_offset = 0;
|
||||
|
||||
++shared_page.date_time_counter;
|
||||
|
||||
// system time is updated hourly
|
||||
CoreTiming::ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, update_time_event);
|
||||
CoreTiming::ScheduleEvent(CoreTiming::msToCycles(60 * 60 * 1000) - cycles_late,
|
||||
update_time_event);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
|
||||
@@ -59,12 +59,12 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
|
||||
dst_bytes_per_pixel, src_buffer, dst_buffer, true,
|
||||
regs.src.block_height);
|
||||
regs.src.BlockHeight());
|
||||
} else {
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
|
||||
dst_bytes_per_pixel, dst_buffer, src_buffer, false,
|
||||
regs.dst.block_height);
|
||||
regs.dst.BlockHeight());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ public:
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
|
||||
u32 BlockHeight() const {
|
||||
// The block height is stored in log2 format.
|
||||
return 1 << block_height;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
|
||||
|
||||
|
||||
@@ -208,6 +208,16 @@ void Maxwell3D::DrawArrays() {
|
||||
|
||||
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
|
||||
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed);
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
|
||||
// it's possible that it is incorrect and that there is some other register used to specify the
|
||||
// drawing mode.
|
||||
if (is_indexed) {
|
||||
regs.index_array.count = 0;
|
||||
} else {
|
||||
regs.vertex_buffer.count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
|
||||
@@ -25,6 +25,13 @@ struct Register {
|
||||
/// Register 255 is special cased to always be 0
|
||||
static constexpr size_t ZeroIndex = 255;
|
||||
|
||||
enum class Size : u64 {
|
||||
Byte = 0,
|
||||
Short = 1,
|
||||
Word = 2,
|
||||
Long = 3,
|
||||
};
|
||||
|
||||
constexpr Register() = default;
|
||||
|
||||
constexpr Register(u64 value) : value(value) {}
|
||||
@@ -236,6 +243,15 @@ union Instruction {
|
||||
BitField<56, 1, u64> neg_imm;
|
||||
} fset;
|
||||
|
||||
union {
|
||||
BitField<10, 2, Register::Size> size;
|
||||
BitField<13, 1, u64> is_signed;
|
||||
BitField<41, 2, u64> selector;
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
BitField<50, 1, u64> saturate_a;
|
||||
} conversion;
|
||||
|
||||
BitField<61, 1, u64> is_b_imm;
|
||||
BitField<60, 1, u64> is_b_gpr;
|
||||
BitField<59, 1, u64> is_c_gpr;
|
||||
@@ -290,10 +306,11 @@ public:
|
||||
MOV_C,
|
||||
MOV_R,
|
||||
MOV_IMM,
|
||||
MOV32I,
|
||||
MOV32_IMM,
|
||||
SHR_C,
|
||||
SHR_R,
|
||||
SHR_IMM,
|
||||
FMNMX,
|
||||
FSETP_C, // Set Predicate
|
||||
FSETP_R,
|
||||
FSETP_IMM,
|
||||
@@ -314,6 +331,7 @@ public:
|
||||
FloatSet,
|
||||
FloatSetPredicate,
|
||||
IntegerSetPredicate,
|
||||
Conversion,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
@@ -435,20 +453,21 @@ private:
|
||||
INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"),
|
||||
INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"),
|
||||
INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"),
|
||||
INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"),
|
||||
INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"),
|
||||
INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Arithmetic, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Arithmetic, "I2I_R"),
|
||||
INST("01110001-1000---", Id::I2I_IMM, Type::Arithmetic, "I2I_IMM"),
|
||||
INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"),
|
||||
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
|
||||
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
|
||||
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
|
||||
INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"),
|
||||
INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"),
|
||||
INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"),
|
||||
INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"),
|
||||
INST("0101110001100---", Id::FMNMX, Type::Arithmetic, "FMNMX"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
|
||||
INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
|
||||
INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
|
||||
INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
|
||||
INST("01011000--------", Id::FSET_R, Type::FloatSet, "FSET_R"),
|
||||
INST("0100100---------", Id::FSET_C, Type::FloatSet, "FSET_C"),
|
||||
INST("0011000---------", Id::FSET_IMM, Type::FloatSet, "FSET_IMM"),
|
||||
|
||||
@@ -153,85 +153,61 @@ private:
|
||||
*/
|
||||
class GLSLRegister {
|
||||
public:
|
||||
GLSLRegister(size_t index, ShaderWriter& shader)
|
||||
: index{index}, shader{shader}, float_str{"freg_" + std::to_string(index)},
|
||||
integer_str{"ireg_" + std::to_string(index)} {}
|
||||
enum class Type {
|
||||
Float,
|
||||
Integer,
|
||||
UnsignedInteger,
|
||||
};
|
||||
|
||||
/// Returns a GLSL string representing the current state of the register
|
||||
const std::string& GetActiveString() {
|
||||
declr_type.insert(active_type);
|
||||
GLSLRegister(size_t index, ShaderWriter& shader) : index{index}, shader{shader} {}
|
||||
|
||||
switch (active_type) {
|
||||
/// Gets the GLSL type string for a register
|
||||
static std::string GetTypeString(Type type) {
|
||||
switch (type) {
|
||||
case Type::Float:
|
||||
return float_str;
|
||||
return "float";
|
||||
case Type::Integer:
|
||||
return integer_str;
|
||||
return "int";
|
||||
case Type::UnsignedInteger:
|
||||
return "uint";
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return float_str;
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Returns a GLSL string representing the register as a float
|
||||
const std::string& GetFloatString() const {
|
||||
ASSERT(IsFloatUsed());
|
||||
return float_str;
|
||||
/// Gets the GLSL register prefix string, used for declarations and referencing
|
||||
static std::string GetPrefixString(Type type) {
|
||||
return "reg_" + GetTypeString(type) + '_';
|
||||
}
|
||||
|
||||
/// Returns a GLSL string representing the register as an integer
|
||||
const std::string& GetIntegerString() const {
|
||||
ASSERT(IsIntegerUsed());
|
||||
return integer_str;
|
||||
/// Returns a GLSL string representing the current state of the register
|
||||
const std::string GetActiveString() {
|
||||
declr_type.insert(active_type);
|
||||
return GetPrefixString(active_type) + std::to_string(index);
|
||||
}
|
||||
|
||||
/// Convert the current register state from float to integer
|
||||
void FloatToInteger() {
|
||||
ASSERT(active_type == Type::Float);
|
||||
|
||||
const std::string src = GetActiveString();
|
||||
active_type = Type::Integer;
|
||||
const std::string dest = GetActiveString();
|
||||
|
||||
shader.AddLine(dest + " = floatBitsToInt(" + src + ");");
|
||||
}
|
||||
|
||||
/// Convert the current register state from integer to float
|
||||
void IntegerToFloat() {
|
||||
ASSERT(active_type == Type::Integer);
|
||||
|
||||
const std::string src = GetActiveString();
|
||||
active_type = Type::Float;
|
||||
const std::string dest = GetActiveString();
|
||||
|
||||
shader.AddLine(dest + " = intBitsToFloat(" + src + ");");
|
||||
}
|
||||
|
||||
/// Returns true if the register was ever used as a float, used for register declarations
|
||||
bool IsFloatUsed() const {
|
||||
return declr_type.find(Type::Float) != declr_type.end();
|
||||
}
|
||||
|
||||
/// Returns true if the register was ever used as an integer, used for register declarations
|
||||
bool IsIntegerUsed() const {
|
||||
return declr_type.find(Type::Integer) != declr_type.end();
|
||||
}
|
||||
|
||||
/// Returns true if the active type is float
|
||||
/// Returns true if the active type is a float
|
||||
bool IsFloat() const {
|
||||
return active_type == Type::Float;
|
||||
}
|
||||
|
||||
/// Returns true if the active type is integer
|
||||
/// Returns true if the active type is an integer
|
||||
bool IsInteger() const {
|
||||
return active_type == Type::Integer;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Type {
|
||||
Float,
|
||||
Integer,
|
||||
};
|
||||
/// Returns the index of the register
|
||||
size_t GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Returns a set of the declared types of the register
|
||||
const std::set<Type>& DeclaredTypes() const {
|
||||
return declr_type;
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t index;
|
||||
const std::string float_str;
|
||||
const std::string integer_str;
|
||||
@@ -254,18 +230,35 @@ public:
|
||||
BuildRegisterList();
|
||||
}
|
||||
|
||||
/// Generates code representing a temporary (GPR) register.
|
||||
std::string GetRegister(const Register& reg, unsigned elem = 0) {
|
||||
if (reg == Register::ZeroIndex) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
return regs[reg.GetSwizzledIndex(elem)].GetActiveString();
|
||||
/**
|
||||
* Gets a register as an float.
|
||||
* @param reg The register to get.
|
||||
* @param elem The element to use for the operation.
|
||||
* @returns GLSL string corresponding to the register as a float.
|
||||
*/
|
||||
std::string GetRegisterAsFloat(const Register& reg, unsigned elem = 0) {
|
||||
ASSERT(regs[reg].IsFloat());
|
||||
return GetRegister(reg, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes code that does a register assignment to float value operation. Should only be used
|
||||
* with shader instructions that deal with floats.
|
||||
* Gets a register as an integer.
|
||||
* @param reg The register to get.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param is_signed Whether to get the register as a signed (or unsigned) integer.
|
||||
* @returns GLSL string corresponding to the register as an integer.
|
||||
*/
|
||||
std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0,
|
||||
bool is_signed = true) {
|
||||
const std::string func = GetGLSLConversionFunc(
|
||||
GLSLRegister::Type::Float,
|
||||
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger);
|
||||
|
||||
return func + '(' + GetRegister(reg, elem) + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes code that does a register assignment to float value operation.
|
||||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param value The code representing the value to assign.
|
||||
@@ -277,21 +270,28 @@ public:
|
||||
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
|
||||
u64 dest_num_components, u64 value_num_components, bool is_abs = false,
|
||||
u64 dest_elem = 0) {
|
||||
ASSERT(regs[reg].IsFloat());
|
||||
SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem);
|
||||
}
|
||||
|
||||
std::string dest = GetRegister(reg, dest_elem);
|
||||
if (dest_num_components > 1) {
|
||||
dest += GetSwizzle(elem);
|
||||
}
|
||||
/**
|
||||
* Writes code that does a register assignment to integer value operation.
|
||||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param value The code representing the value to assign.
|
||||
* @param dest_num_components Number of components in the destination.
|
||||
* @param value_num_components Number of components in the value.
|
||||
* @param is_abs Optional, when True, applies absolute value to output.
|
||||
* @param dest_elem Optional, the destination element to use for the operation.
|
||||
*/
|
||||
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
|
||||
const std::string& value, u64 dest_num_components,
|
||||
u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) {
|
||||
const std::string func = GetGLSLConversionFunc(
|
||||
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger,
|
||||
GLSLRegister::Type::Float);
|
||||
|
||||
std::string src = '(' + value + ')';
|
||||
if (value_num_components > 1) {
|
||||
src += GetSwizzle(elem);
|
||||
}
|
||||
|
||||
src = is_abs ? "abs(" + src + ')' : src;
|
||||
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components,
|
||||
is_abs, dest_elem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +302,7 @@ public:
|
||||
* @param attribute The input attibute to use as the source value.
|
||||
*/
|
||||
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute) {
|
||||
std::string dest = GetRegister(reg);
|
||||
std::string dest = GetRegisterAsFloat(reg);
|
||||
std::string src = GetInputAttribute(attribute) + GetSwizzle(elem);
|
||||
|
||||
if (regs[reg].IsFloat()) {
|
||||
@@ -323,7 +323,7 @@ public:
|
||||
*/
|
||||
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
|
||||
std::string dest = GetOutputAttribute(attribute) + GetSwizzle(elem);
|
||||
std::string src = GetRegister(reg);
|
||||
std::string src = GetRegisterAsFloat(reg);
|
||||
ASSERT_MSG(regs[reg].IsFloat(), "Output attributes must be set to a float");
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
}
|
||||
@@ -347,11 +347,10 @@ public:
|
||||
/// Add declarations for registers
|
||||
void GenerateDeclarations() {
|
||||
for (const auto& reg : regs) {
|
||||
if (reg.IsFloatUsed()) {
|
||||
declarations.AddLine("float " + reg.GetFloatString() + " = 0.0;");
|
||||
}
|
||||
if (reg.IsIntegerUsed()) {
|
||||
declarations.AddLine("int " + reg.GetIntegerString() + " = 0;");
|
||||
for (const auto& type : reg.DeclaredTypes()) {
|
||||
declarations.AddLine(GLSLRegister::GetTypeString(type) + ' ' +
|
||||
GLSLRegister::GetPrefixString(type) +
|
||||
std::to_string(reg.GetIndex()) + " = 0;");
|
||||
}
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
@@ -395,6 +394,51 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc.
|
||||
const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const {
|
||||
const std::string src_type = GLSLRegister::GetTypeString(src);
|
||||
std::string dest_type = GLSLRegister::GetTypeString(dest);
|
||||
dest_type[0] = toupper(dest_type[0]);
|
||||
return src_type + "BitsTo" + dest_type;
|
||||
}
|
||||
|
||||
/// Generates code representing a temporary (GPR) register.
|
||||
std::string GetRegister(const Register& reg, unsigned elem) {
|
||||
if (reg == Register::ZeroIndex) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
return regs[reg.GetSwizzledIndex(elem)].GetActiveString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes code that does a register assignment to value operation.
|
||||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @param value The code representing the value to assign.
|
||||
* @param dest_num_components Number of components in the destination.
|
||||
* @param value_num_components Number of components in the value.
|
||||
* @param is_abs Optional, when True, applies absolute value to output.
|
||||
* @param dest_elem Optional, the destination element to use for the operation.
|
||||
*/
|
||||
void SetRegister(const Register& reg, u64 elem, const std::string& value,
|
||||
u64 dest_num_components, u64 value_num_components, bool is_abs,
|
||||
u64 dest_elem) {
|
||||
std::string dest = GetRegister(reg, dest_elem);
|
||||
if (dest_num_components > 1) {
|
||||
dest += GetSwizzle(elem);
|
||||
}
|
||||
|
||||
std::string src = '(' + value + ')';
|
||||
if (value_num_components > 1) {
|
||||
src += GetSwizzle(elem);
|
||||
}
|
||||
|
||||
src = is_abs ? "abs(" + src + ')' : src;
|
||||
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
}
|
||||
|
||||
/// Build the GLSL register list.
|
||||
void BuildRegisterList() {
|
||||
for (size_t index = 0; index < Register::NumRegisters; ++index) {
|
||||
@@ -598,7 +642,7 @@ private:
|
||||
switch (opcode->GetType()) {
|
||||
case OpCode::Type::Arithmetic: {
|
||||
std::string op_a = instr.alu.negate_a ? "-" : "";
|
||||
op_a += regs.GetRegister(instr.gpr8);
|
||||
op_a += regs.GetRegisterAsFloat(instr.gpr8);
|
||||
if (instr.alu.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
}
|
||||
@@ -609,7 +653,7 @@ private:
|
||||
op_b += GetImmediate19(instr);
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b += regs.GetRegister(instr.gpr20);
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr20);
|
||||
} else {
|
||||
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
|
||||
}
|
||||
@@ -620,6 +664,17 @@ private:
|
||||
}
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::MOV_C:
|
||||
case OpCode::Id::MOV_R: {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::Id::MOV32_IMM: {
|
||||
// mov32i doesn't have abs or neg bits.
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL_C:
|
||||
case OpCode::Id::FMUL_R:
|
||||
case OpCode::Id::FMUL_IMM: {
|
||||
@@ -629,8 +684,8 @@ private:
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
// fmul32i doesn't have abs or neg bits.
|
||||
regs.SetRegisterToFloat(
|
||||
instr.gpr0, 0, regs.GetRegister(instr.gpr8) + " * " + GetImmediate32(instr), 1,
|
||||
1);
|
||||
instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD_C:
|
||||
@@ -687,29 +742,29 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Ffma: {
|
||||
std::string op_a = regs.GetRegister(instr.gpr8);
|
||||
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string op_b = instr.ffma.negate_b ? "-" : "";
|
||||
std::string op_c = instr.ffma.negate_c ? "-" : "";
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::FFMA_CR: {
|
||||
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
|
||||
op_c += regs.GetRegister(instr.gpr39);
|
||||
op_c += regs.GetRegisterAsFloat(instr.gpr39);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FFMA_RR: {
|
||||
op_b += regs.GetRegister(instr.gpr20);
|
||||
op_c += regs.GetRegister(instr.gpr39);
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr20);
|
||||
op_c += regs.GetRegisterAsFloat(instr.gpr39);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FFMA_RC: {
|
||||
op_b += regs.GetRegister(instr.gpr39);
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr39);
|
||||
op_c += regs.GetUniform(instr.uniform, instr.gpr0);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FFMA_IMM: {
|
||||
op_b += GetImmediate19(instr);
|
||||
op_c += regs.GetRegister(instr.gpr39);
|
||||
op_c += regs.GetRegisterAsFloat(instr.gpr39);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -721,6 +776,32 @@ private:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Conversion: {
|
||||
ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented");
|
||||
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
|
||||
ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
|
||||
ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented");
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::I2I_R:
|
||||
case OpCode::Id::I2F_R: {
|
||||
std::string op_a =
|
||||
regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
|
||||
|
||||
if (instr.conversion.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
}
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName());
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Memory: {
|
||||
const Attribute::Index attribute = instr.attribute.fmt20.index;
|
||||
|
||||
@@ -739,8 +820,8 @@ private:
|
||||
}
|
||||
case OpCode::Id::TEXS: {
|
||||
ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested");
|
||||
const std::string op_a = regs.GetRegister(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegister(instr.gpr20);
|
||||
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
// Add an extra scope and declare the texture coords inside to prevent overwriting
|
||||
@@ -765,7 +846,7 @@ private:
|
||||
}
|
||||
case OpCode::Type::FloatSetPredicate: {
|
||||
std::string op_a = instr.fsetp.neg_a ? "-" : "";
|
||||
op_a += regs.GetRegister(instr.gpr8);
|
||||
op_a += regs.GetRegisterAsFloat(instr.gpr8);
|
||||
|
||||
if (instr.fsetp.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
@@ -781,7 +862,7 @@ private:
|
||||
op_b += '(' + GetImmediate19(instr) + ')';
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b += regs.GetRegister(instr.gpr20);
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr20);
|
||||
} else {
|
||||
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
|
||||
}
|
||||
@@ -807,6 +888,9 @@ private:
|
||||
case PredCondition::Equal:
|
||||
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')');
|
||||
break;
|
||||
case PredCondition::LessEqual:
|
||||
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") <= (" + op_b + ')');
|
||||
break;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})",
|
||||
static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b);
|
||||
@@ -816,7 +900,7 @@ private:
|
||||
}
|
||||
case OpCode::Type::FloatSet: {
|
||||
std::string op_a = instr.fset.neg_a ? "-" : "";
|
||||
op_a += regs.GetRegister(instr.gpr8);
|
||||
op_a += regs.GetRegisterAsFloat(instr.gpr8);
|
||||
|
||||
if (instr.fset.abs_a) {
|
||||
op_a = "abs(" + op_a + ')';
|
||||
@@ -832,7 +916,7 @@ private:
|
||||
op_b += imm;
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b += regs.GetRegister(instr.gpr20);
|
||||
op_b += regs.GetRegisterAsFloat(instr.gpr20);
|
||||
} else {
|
||||
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
|
||||
}
|
||||
@@ -858,6 +942,10 @@ private:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") == (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
case PredCondition::LessEqual:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") <= (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
case PredCondition::GreaterThan:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") > (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
@@ -877,10 +965,10 @@ private:
|
||||
|
||||
// Final color output is currently hardcoded to GPR0-3 for fragment shaders
|
||||
if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
|
||||
shader.AddLine("color.r = " + regs.GetRegister(0) + ';');
|
||||
shader.AddLine("color.g = " + regs.GetRegister(1) + ';');
|
||||
shader.AddLine("color.b = " + regs.GetRegister(2) + ';');
|
||||
shader.AddLine("color.a = " + regs.GetRegister(3) + ';');
|
||||
shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';');
|
||||
shader.AddLine("color.g = " + regs.GetRegisterAsFloat(1) + ';');
|
||||
shader.AddLine("color.b = " + regs.GetRegisterAsFloat(2) + ';');
|
||||
shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';');
|
||||
}
|
||||
|
||||
shader.AddLine("return true;");
|
||||
|
||||
@@ -36,6 +36,18 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
return {};
|
||||
}
|
||||
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm: {
|
||||
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return GL_BYTE;
|
||||
}
|
||||
|
||||
NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
return GL_FLOAT;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -102,8 +104,8 @@ private:
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), child(nullptr), emu_thread(emu_thread) {
|
||||
|
||||
std::string window_title = Common::StringFromFormat("yuzu %s| %s-%s", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
setWindowTitle(QString::fromStdString(window_title));
|
||||
|
||||
InputCommon::Init();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <fmt/format.h>
|
||||
#include <glad/glad.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
@@ -97,8 +98,8 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
||||
|
||||
std::string window_title = Common::StringFromFormat("yuzu %s| %s-%s ", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
|
||||
Reference in New Issue
Block a user