Compare commits

...

21 Commits

Author SHA1 Message Date
Lioncash
3abba08080 string_util: Remove StringFromFormat() and related functions
Given we utilize fmt, we don't need to provide our own functions for formatting anymore
2018-04-29 18:52:33 -04:00
bunnei
869075867b Merge pull request #421 from Subv/sh_pred3
Shaders: Implemented predicate condition 3 (LessEqual) in the fset and fsetp instructions.
2018-04-29 15:59:33 -04:00
Subv
da32c648bf Shaders: Implemented predicate condition 3 (LessEqual) in the fset and fsetp instructions. 2018-04-29 12:49:41 -05:00
bunnei
6c464a2a4a Merge pull request #416 from bunnei/shader-ints-p3
gl_shader_decompiler: Implement MOV32I, partially implement I2I, I2F
2018-04-29 12:56:16 -04:00
bunnei
49d92aa661 Merge pull request #417 from bunnei/lang-codes
set/am: Fix code for getting language codes
2018-04-29 12:55:43 -04:00
bunnei
334e859ab1 am: Fix GetDesiredLanguage implementation. 2018-04-29 11:07:07 -04:00
bunnei
17b16cf6f6 set: Fix GetAvailableLanguageCodes implementation. 2018-04-29 11:07:06 -04:00
Sebastian Valle
faa431b274 Merge pull request #418 from bunnei/copy-block-height
fermi_2d: Fix surface copy block height.
2018-04-29 09:49:33 -05:00
bunnei
f87ea8fa8b fermi_2d: Fix surface copy block height. 2018-04-28 20:40:03 -04:00
bunnei
0c01c34eff gl_shader_decompiler: Partially implement I2I_R, and I2F_R. 2018-04-28 20:03:19 -04:00
bunnei
e73927cfc2 gl_shader_decompiler: More cleanups, etc. with how we handle register types. 2018-04-28 20:03:19 -04:00
bunnei
c691fa4074 GLSLRegister: Simplify register declarations, etc. 2018-04-28 20:03:19 -04:00
bunnei
f2dcb39049 shader_bytecode: Add decodings for i2i instructions. 2018-04-28 20:03:18 -04:00
bunnei
a7b5ab4d9a gl_shader_decompiler: Implement MOV32_IMM instruction. 2018-04-28 20:03:18 -04:00
bunnei
3d9126ba87 Merge pull request #414 from lioncash/cruft
file_util: Remove compiler version checks around is_trivially_copyable
2018-04-28 17:06:49 -04:00
bunnei
7fd54fed92 Merge pull request #413 from lioncash/dynarmic
externals: Update dynarmic
2018-04-28 17:06:17 -04:00
Lioncash
5d9ee12b1a file_util: Remove compiler version checks around is_trivially_copyable()
The minimum clang/GCC versions we support already support this. We can also
remove is_standard_layout(), as fread and fwrite only require the type to be
trivially copyable.
2018-04-28 15:31:23 -04:00
bunnei
bad00085ca Merge pull request #412 from lioncash/log
log: Remove old logging macros and functions
2018-04-28 15:04:24 -04:00
Lioncash
e56e2a1528 externals: Update dynarmic
Just a basic update to keep it in sync
2018-04-28 14:31:37 -04:00
Mat M
99ac33de20 Merge pull request #411 from lioncash/travis
travis: Use Xcode 9.3 instead of 9.2
2018-04-27 22:12:00 -04:00
Lioncash
5a579f66a0 travis: Use Xcode 9.3 instead of 9.2
Keeps the toolchains up to date.
2018-04-27 12:17:01 -04:00
18 changed files with 289 additions and 232 deletions

View File

@@ -23,7 +23,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode9.2
osx_image: xcode9.3
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -170,12 +170,8 @@ public:
template <typename T>
size_t ReadArray(T* data, size_t length) {
static_assert(std::is_standard_layout<T>(),
"Given array does not consist of standard layout objects");
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
#endif
if (!IsOpen()) {
m_good = false;
@@ -191,12 +187,8 @@ public:
template <typename T>
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_standard_layout<T>(),
"Given array does not consist of standard layout objects");
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
#endif
if (!IsOpen()) {
m_good = false;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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
}

View File

@@ -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;

View File

@@ -12,6 +12,7 @@
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/set/set.h"
#include "core/settings.h"
namespace Service::AM {
@@ -537,10 +538,11 @@ void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
}
void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(SystemLanguage::English);
NGLOG_WARNING(Service_AM, "(STUBBED) called");
rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US));
NGLOG_DEBUG(Service_AM, "called");
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {

View File

@@ -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;
}

View File

@@ -14,15 +14,33 @@ namespace Service::Set {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
constexpr std::array<u8, 13> lang_codes{};
ctx.WriteBuffer(lang_codes.data(), lang_codes.size());
IPC::ResponseBuilder rb{ctx, 2};
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
LanguageCode::DE,
LanguageCode::IT,
LanguageCode::ES,
LanguageCode::ZH_CN,
LanguageCode::KO,
LanguageCode::NL,
LanguageCode::PT,
LanguageCode::RU,
LanguageCode::ZH_TW,
LanguageCode::EN_GB,
LanguageCode::FR_CA,
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
}};
ctx.WriteBuffer(available_language_codes.data(), available_language_codes.size());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(available_language_codes.size()));
NGLOG_WARNING(Service_SET, "(STUBBED) called");
NGLOG_DEBUG(Service_SET, "called");
}
SET::SET() : ServiceFramework("set") {

View File

@@ -8,6 +8,27 @@
namespace Service::Set {
/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
enum class LanguageCode : u64 {
JA = 0x000000000000616A,
EN_US = 0x00000053552D6E65,
FR = 0x0000000000007266,
DE = 0x0000000000006564,
IT = 0x0000000000007469,
ES = 0x0000000000007365,
ZH_CN = 0x0000004E432D687A,
KO = 0x0000000000006F6B,
NL = 0x0000000000006C6E,
PT = 0x0000000000007470,
RU = 0x0000000000007572,
ZH_TW = 0x00000057542D687A,
EN_GB = 0x00000042472D6E65,
FR_CA = 0x00000041432D7266,
ES_419 = 0x00003931342D7365,
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
};
class SET final : public ServiceFramework<SET> {
public:
explicit SET();

View File

@@ -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());
}
}

View File

@@ -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");

View File

@@ -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,7 +306,7 @@ public:
MOV_C,
MOV_R,
MOV_IMM,
MOV32I,
MOV32_IMM,
SHR_C,
SHR_R,
SHR_IMM,
@@ -314,6 +330,7 @@ public:
FloatSet,
FloatSetPredicate,
IntegerSetPredicate,
Conversion,
Unknown,
};
@@ -435,20 +452,20 @@ 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("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"),

View File

@@ -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,11 @@ private:
}
switch (opcode->GetId()) {
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 +678,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 +736,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 +770,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 +814,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 +840,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 +856,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 +882,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 +894,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 +910,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 +936,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 +959,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;");

View File

@@ -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();

View File

@@ -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