Compare commits
29 Commits
android-16
...
android-16
Author | SHA1 | Date | |
---|---|---|---|
93d90b5e87 | |||
c2ecdaabb5 | |||
39fe6e4516 | |||
f2e9fcb6a2 | |||
5888f2c3cb | |||
6a1fa9bb17 | |||
1bb76201e6 | |||
372bca5945 | |||
345ec25532 | |||
816c7a8d1f | |||
efe52db690 | |||
d61df0f400 | |||
b14547b8b6 | |||
97ad3e7530 | |||
0589a32f75 | |||
617dc0f822 | |||
53956a2990 | |||
a7731abb72 | |||
50fd029eaa | |||
b770f6a985 | |||
797e8fdbc3 | |||
65e646eeba | |||
6ca530a721 | |||
e01c535178 | |||
7239547ead | |||
7fc06260d1 | |||
fcc85abe27 | |||
6851e93296 | |||
ffbba74c91 |
@ -1,10 +1,9 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [12304](https://github.com/yuzu-emu/yuzu//pull/12304) | [`fcc85abe2`](https://github.com/yuzu-emu/yuzu//pull/12304/files) | nvnflinger: mark buffer as acquired when acquired | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12390](https://github.com/yuzu-emu/yuzu//pull/12390) | [`fba3fa705`](https://github.com/yuzu-emu/yuzu//pull/12390/files) | renderer_vulkan: work around turnip binding bug in a610 | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12349](https://github.com/yuzu-emu/yuzu//pull/12349) | [`6851e9329`](https://github.com/yuzu-emu/yuzu//pull/12349/files) | Have GetActiveChannelCount return the system channels instead of host device channels | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
| [12400](https://github.com/yuzu-emu/yuzu//pull/12400) | [`a2b567dfd`](https://github.com/yuzu-emu/yuzu//pull/12400/files) | vk_query_cache: Fix prefix sum max_accumulation_limit logic | [ameerj](https://github.com/ameerj/) | Yes |
|
||||||
| [12382](https://github.com/yuzu-emu/yuzu//pull/12382) | [`7fc06260d`](https://github.com/yuzu-emu/yuzu//pull/12382/files) | renderer_vulkan: allow up to 7 swapchain images | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12403](https://github.com/yuzu-emu/yuzu//pull/12403) | [`fcfa8b680`](https://github.com/yuzu-emu/yuzu//pull/12403/files) | shader_recompiler: use minimal clip distance array | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12387](https://github.com/yuzu-emu/yuzu//pull/12387) | [`6ca530a72`](https://github.com/yuzu-emu/yuzu//pull/12387/files) | android: add oboe audio sink | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12409](https://github.com/yuzu-emu/yuzu//pull/12409) | [`93c19a40b`](https://github.com/yuzu-emu/yuzu//pull/12409/files) | nce: fix read size in simd immediate emulation | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12391](https://github.com/yuzu-emu/yuzu//pull/12391) | [`65e646eeb`](https://github.com/yuzu-emu/yuzu//pull/12391/files) | Revert "video_core: use interval map for page count tracking" | [liamwhite](https://github.com/liamwhite/) | Yes |
|
|
||||||
|
|
||||||
|
|
||||||
End of merge log. You can find the original README.md below the break.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~OboeSinkStream() override {
|
~OboeSinkStream() override {
|
||||||
LOG_DEBUG(Audio_Sink, "Destructing Oboe stream {}", name);
|
LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Finalize() override {
|
void Finalize() override {
|
||||||
@ -66,11 +66,7 @@ public:
|
|||||||
std::shared_ptr<oboe::AudioStream> temp_stream;
|
std::shared_ptr<oboe::AudioStream> temp_stream;
|
||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
|
|
||||||
const auto result = builder.setDirection(direction)
|
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
|
||||||
->setSampleRate(TargetSampleRate)
|
|
||||||
->setFormat(oboe::AudioFormat::I16)
|
|
||||||
->setFormatConversionAllowed(true)
|
|
||||||
->openStream(temp_stream);
|
|
||||||
ASSERT(result == oboe::Result::OK);
|
ASSERT(result == oboe::Result::OK);
|
||||||
|
|
||||||
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
||||||
@ -105,6 +101,20 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
|
||||||
|
oboe::Direction direction) {
|
||||||
|
// TODO: investigate callback delay issues when using AAudio
|
||||||
|
return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||||
|
->setAudioApi(oboe::AudioApi::OpenSLES)
|
||||||
|
->setDirection(direction)
|
||||||
|
->setSampleRate(TargetSampleRate)
|
||||||
|
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||||
|
->setFormat(oboe::AudioFormat::I16)
|
||||||
|
->setFormatConversionAllowed(true)
|
||||||
|
->setUsage(oboe::Usage::Game)
|
||||||
|
->setBufferCapacityInFrames(TargetSampleCount * 2);
|
||||||
|
}
|
||||||
|
|
||||||
bool OpenStream() {
|
bool OpenStream() {
|
||||||
const auto direction = [&]() {
|
const auto direction = [&]() {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -135,12 +145,10 @@ private:
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
const auto result = builder.setDirection(direction)
|
const auto result = ConfigureBuilder(builder, direction)
|
||||||
->setSampleRate(TargetSampleRate)
|
|
||||||
->setChannelCount(expected_channels)
|
->setChannelCount(expected_channels)
|
||||||
->setChannelMask(expected_mask)
|
->setChannelMask(expected_mask)
|
||||||
->setFormat(oboe::AudioFormat::I16)
|
->setChannelConversionAllowed(true)
|
||||||
->setFormatConversionAllowed(true)
|
|
||||||
->setDataCallback(this)
|
->setDataCallback(this)
|
||||||
->setErrorCallback(this)
|
->setErrorCallback(this)
|
||||||
->openStream(m_stream);
|
->openStream(m_stream);
|
||||||
@ -151,8 +159,16 @@ private:
|
|||||||
bool SetStreamProperties() {
|
bool SetStreamProperties() {
|
||||||
ASSERT(m_stream);
|
ASSERT(m_stream);
|
||||||
|
|
||||||
|
m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
|
||||||
device_channels = m_stream->getChannelCount();
|
device_channels = m_stream->getChannelCount();
|
||||||
LOG_INFO(Audio_Sink, "Opened Oboe stream with {} channels", device_channels);
|
|
||||||
|
const auto sample_rate = m_stream->getSampleRate();
|
||||||
|
const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
|
||||||
|
const auto stream_backend =
|
||||||
|
m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
|
||||||
|
|
||||||
|
LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
|
||||||
|
stream_backend, device_channels, sample_rate, buffer_capacity);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
constexpr u32 StackSize = 32_KiB;
|
constexpr u32 StackSize = 128_KiB;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -249,6 +249,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size in bytes
|
||||||
const u64 size = 4 << opc.ZeroExtend();
|
const u64 size = 4 << opc.ZeroExtend();
|
||||||
const u64 offset = imm19.SignExtend<u64>() << 2;
|
const u64 offset = imm19.SignExtend<u64>() << 2;
|
||||||
const u64 address = this->GetPc() + offset;
|
const u64 address = this->GetPc() + offset;
|
||||||
@ -530,7 +531,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
|
|||||||
}
|
}
|
||||||
case MemOp::Load: {
|
case MemOp::Load: {
|
||||||
u128 data{};
|
u128 data{};
|
||||||
m_memory.ReadBlock(address, &data, datasize);
|
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||||
this->SetVec(Vt, data);
|
this->SetVec(Vt, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,13 @@ enum class ImageDirectoryId : u32 {
|
|||||||
SdCard,
|
SdCard,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OpenDirectoryMode : u64 {
|
||||||
|
Directory = (1 << 0),
|
||||||
|
File = (1 << 1),
|
||||||
|
All = Directory | File
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
|
||||||
|
|
||||||
class FileSystemController {
|
class FileSystemController {
|
||||||
public:
|
public:
|
||||||
explicit FileSystemController(Core::System& system_);
|
explicit FileSystemController(Core::System& system_);
|
||||||
|
@ -259,7 +259,7 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
|
|||||||
|
|
||||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||||
public:
|
public:
|
||||||
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
|
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, OpenDirectoryMode mode)
|
||||||
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
|
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IDirectory::Read, "Read"},
|
{0, &IDirectory::Read, "Read"},
|
||||||
@ -269,9 +269,13 @@ public:
|
|||||||
|
|
||||||
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||||
// Build entry index now to save time later.
|
// Build entry index now to save time later.
|
||||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
|
if (True(mode & OpenDirectoryMode::Directory)) {
|
||||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
|
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
|
||||||
}
|
}
|
||||||
|
if (True(mode & OpenDirectoryMode::File)) {
|
||||||
|
BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSys::VirtualDir backend;
|
FileSys::VirtualDir backend;
|
||||||
@ -446,11 +450,9 @@ public:
|
|||||||
|
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
const auto file_buffer = ctx.ReadBuffer();
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||||
|
const auto mode = rp.PopRaw<OpenDirectoryMode>();
|
||||||
|
|
||||||
// TODO(Subv): Implement this filter.
|
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
|
||||||
const u32 filter_flags = rp.Pop<u32>();
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
|
|
||||||
|
|
||||||
FileSys::VirtualDir vfs_dir{};
|
FileSys::VirtualDir vfs_dir{};
|
||||||
auto result = backend.OpenDirectory(&vfs_dir, name);
|
auto result = backend.OpenDirectory(&vfs_dir, name);
|
||||||
@ -460,7 +462,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto directory = std::make_shared<IDirectory>(system, vfs_dir);
|
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
@ -74,6 +74,11 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
|||||||
case IR::Attribute::ClipDistance7: {
|
case IR::Attribute::ClipDistance7: {
|
||||||
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||||
const u32 index{static_cast<u32>(attr) - base};
|
const u32 index{static_cast<u32>(attr) - base};
|
||||||
|
if (index >= ctx.profile.max_user_clip_distances) {
|
||||||
|
LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
|
||||||
|
ctx.profile.max_user_clip_distances);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
const Id clip_num{ctx.Const(index)};
|
const Id clip_num{ctx.Const(index)};
|
||||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
||||||
}
|
}
|
||||||
|
@ -1528,7 +1528,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
|||||||
if (stage == Stage::Fragment) {
|
if (stage == Stage::Fragment) {
|
||||||
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
||||||
}
|
}
|
||||||
const Id type{TypeArray(F32[1], Const(8U))};
|
const Id type{TypeArray(
|
||||||
|
F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
|
||||||
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
|
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
|
||||||
}
|
}
|
||||||
if (info.stores[IR::Attribute::Layer] &&
|
if (info.stores[IR::Attribute::Layer] &&
|
||||||
|
@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
|
|||||||
}
|
}
|
||||||
for (size_t index = 0; index < 8; ++index) {
|
for (size_t index = 0; index < 8; ++index) {
|
||||||
const u16 mask{header.vtg.omap_systemc.clip_distances};
|
const u16 mask{header.vtg.omap_systemc.clip_distances};
|
||||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
|
const bool used{((mask >> index) & 1) != 0};
|
||||||
|
info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
|
||||||
|
if (used) {
|
||||||
|
info.used_clip_distances = static_cast<u32>(index) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
info.stores.Set(IR::Attribute::PrimitiveId,
|
info.stores.Set(IR::Attribute::PrimitiveId,
|
||||||
header.vtg.omap_systemb.primitive_array_id != 0);
|
header.vtg.omap_systemb.primitive_array_id != 0);
|
||||||
|
@ -87,6 +87,8 @@ struct Profile {
|
|||||||
bool has_broken_robust{};
|
bool has_broken_robust{};
|
||||||
|
|
||||||
u64 min_ssbo_alignment{};
|
u64 min_ssbo_alignment{};
|
||||||
|
|
||||||
|
u32 max_user_clip_distances{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
@ -227,6 +227,8 @@ struct Info {
|
|||||||
bool requires_layer_emulation{};
|
bool requires_layer_emulation{};
|
||||||
IR::Attribute emulated_layer{};
|
IR::Attribute emulated_layer{};
|
||||||
|
|
||||||
|
u32 used_clip_distances{};
|
||||||
|
|
||||||
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
|
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
|
||||||
constant_buffer_descriptors;
|
constant_buffer_descriptors;
|
||||||
boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
|
boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
|
||||||
|
@ -58,6 +58,9 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
|
|||||||
glObjectLabel(GL_BUFFER, buffer.handle, static_cast<GLsizei>(name.size()), name.data());
|
glObjectLabel(GL_BUFFER, buffer.handle, static_cast<GLsizei>(name.size()), name.data());
|
||||||
}
|
}
|
||||||
glNamedBufferData(buffer.handle, SizeBytes(), nullptr, GL_DYNAMIC_DRAW);
|
glNamedBufferData(buffer.handle, SizeBytes(), nullptr, GL_DYNAMIC_DRAW);
|
||||||
|
if (runtime.has_unified_vertex_buffers) {
|
||||||
|
glGetNamedBufferParameterui64vNV(buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::ImmediateUpload(size_t offset, std::span<const u8> data) noexcept {
|
void Buffer::ImmediateUpload(size_t offset, std::span<const u8> data) noexcept {
|
||||||
@ -109,6 +112,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_,
|
|||||||
: device{device_}, staging_buffer_pool{staging_buffer_pool_},
|
: device{device_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
has_fast_buffer_sub_data{device.HasFastBufferSubData()},
|
has_fast_buffer_sub_data{device.HasFastBufferSubData()},
|
||||||
use_assembly_shaders{device.UseAssemblyShaders()},
|
use_assembly_shaders{device.UseAssemblyShaders()},
|
||||||
|
has_unified_vertex_buffers{device.HasVertexBufferUnifiedMemory()},
|
||||||
stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} {
|
stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} {
|
||||||
GLint gl_max_attributes;
|
GLint gl_max_attributes;
|
||||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_attributes);
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_attributes);
|
||||||
@ -210,8 +214,14 @@ void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
|
void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
|
||||||
|
if (has_unified_vertex_buffers) {
|
||||||
|
buffer.MakeResident(GL_READ_ONLY);
|
||||||
|
glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset,
|
||||||
|
static_cast<GLsizeiptr>(Common::AlignUp(size, 4)));
|
||||||
|
} else {
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
|
||||||
index_buffer_offset = offset;
|
index_buffer_offset = offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
||||||
@ -219,8 +229,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
|
|||||||
if (index >= max_attributes) {
|
if (index >= max_attributes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (has_unified_vertex_buffers) {
|
||||||
|
buffer.MakeResident(GL_READ_ONLY);
|
||||||
|
glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
|
||||||
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
|
||||||
|
buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
|
||||||
|
} else {
|
||||||
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
||||||
static_cast<GLsizei>(stride));
|
static_cast<GLsizei>(stride));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
@ -233,9 +250,23 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
|
|||||||
[](u64 stride) { return static_cast<GLsizei>(stride); });
|
[](u64 stride) { return static_cast<GLsizei>(stride); });
|
||||||
const u32 count =
|
const u32 count =
|
||||||
std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index);
|
std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index);
|
||||||
|
if (has_unified_vertex_buffers) {
|
||||||
|
for (u32 index = 0; index < count; ++index) {
|
||||||
|
Buffer& buffer = *bindings.buffers[index];
|
||||||
|
buffer.MakeResident(GL_READ_ONLY);
|
||||||
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, bindings.min_index + index,
|
||||||
|
buffer.HostGpuAddr() + bindings.offsets[index],
|
||||||
|
static_cast<GLsizeiptr>(bindings.sizes[index]));
|
||||||
|
}
|
||||||
|
static constexpr std::array<size_t, 32> ZEROS{};
|
||||||
|
glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count),
|
||||||
|
reinterpret_cast<const GLuint*>(ZEROS.data()),
|
||||||
|
reinterpret_cast<const GLintptr*>(ZEROS.data()), buffer_strides.data());
|
||||||
|
} else {
|
||||||
glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
|
glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
|
||||||
reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
|
reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
|
||||||
buffer_strides.data());
|
buffer_strides.data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
|
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
|
||||||
|
@ -209,6 +209,7 @@ private:
|
|||||||
|
|
||||||
bool has_fast_buffer_sub_data = false;
|
bool has_fast_buffer_sub_data = false;
|
||||||
bool use_assembly_shaders = false;
|
bool use_assembly_shaders = false;
|
||||||
|
bool has_unified_vertex_buffers = false;
|
||||||
|
|
||||||
bool use_storage_buffers = false;
|
bool use_storage_buffers = false;
|
||||||
|
|
||||||
|
@ -200,6 +200,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
|
|||||||
has_broken_texture_view_formats = is_amd || (!is_linux && is_intel);
|
has_broken_texture_view_formats = is_amd || (!is_linux && is_intel);
|
||||||
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
|
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
|
||||||
has_derivative_control = GLAD_GL_ARB_derivative_control;
|
has_derivative_control = GLAD_GL_ARB_derivative_control;
|
||||||
|
has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
|
||||||
has_debugging_tool_attached = IsDebugToolAttached(extensions);
|
has_debugging_tool_attached = IsDebugToolAttached(extensions);
|
||||||
has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
|
has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
|
||||||
has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough;
|
has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough;
|
||||||
|
@ -72,6 +72,10 @@ public:
|
|||||||
return has_texture_shadow_lod;
|
return has_texture_shadow_lod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasVertexBufferUnifiedMemory() const {
|
||||||
|
return has_vertex_buffer_unified_memory;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasASTC() const {
|
bool HasASTC() const {
|
||||||
return has_astc;
|
return has_astc;
|
||||||
}
|
}
|
||||||
@ -211,6 +215,7 @@ private:
|
|||||||
bool has_vertex_viewport_layer{};
|
bool has_vertex_viewport_layer{};
|
||||||
bool has_image_load_formatted{};
|
bool has_image_load_formatted{};
|
||||||
bool has_texture_shadow_lod{};
|
bool has_texture_shadow_lod{};
|
||||||
|
bool has_vertex_buffer_unified_memory{};
|
||||||
bool has_astc{};
|
bool has_astc{};
|
||||||
bool has_variable_aoffi{};
|
bool has_variable_aoffi{};
|
||||||
bool has_component_indexing_bug{};
|
bool has_component_indexing_bug{};
|
||||||
|
@ -162,14 +162,18 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
|
|||||||
SyncFramebufferSRGB();
|
SyncFramebufferSRGB();
|
||||||
}
|
}
|
||||||
if (regs.clear_surface.Z) {
|
if (regs.clear_surface.Z) {
|
||||||
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
|
if (regs.zeta_enable != 0) {
|
||||||
|
LOG_DEBUG(Render_OpenGL, "Tried to clear Z but buffer is not enabled!");
|
||||||
|
}
|
||||||
use_depth = true;
|
use_depth = true;
|
||||||
|
|
||||||
state_tracker.NotifyDepthMask();
|
state_tracker.NotifyDepthMask();
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
}
|
}
|
||||||
if (regs.clear_surface.S) {
|
if (regs.clear_surface.S) {
|
||||||
ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
|
if (regs.zeta_enable) {
|
||||||
|
LOG_DEBUG(Render_OpenGL, "Tried to clear stencil but buffer is not enabled!");
|
||||||
|
}
|
||||||
use_stencil = true;
|
use_stencil = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1294,15 +1298,13 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum
|
|||||||
program->ConfigureTransformFeedback();
|
program->ConfigureTransformFeedback();
|
||||||
|
|
||||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
|
||||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
|
||||||
UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
|
|
||||||
|
|
||||||
// We may have to call BeginTransformFeedbackNV here since they seem to call different
|
// We may have to call BeginTransformFeedbackNV here since they seem to call different
|
||||||
// implementations on Nvidia's driver (the pointer is different) but we are using
|
// implementations on Nvidia's driver (the pointer is different) but we are using
|
||||||
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
|
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
|
||||||
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
|
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
|
||||||
glBeginTransformFeedback(GL_POINTS);
|
glBeginTransformFeedback(primitive_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::EndTransformFeedback() {
|
void RasterizerOpenGL::EndTransformFeedback() {
|
||||||
|
@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
|||||||
.ignore_nan_fp_comparisons = true,
|
.ignore_nan_fp_comparisons = true,
|
||||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||||
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
||||||
|
.max_user_clip_distances = 8,
|
||||||
},
|
},
|
||||||
host_info{
|
host_info{
|
||||||
.support_float64 = true,
|
.support_float64 = true,
|
||||||
|
@ -168,6 +168,14 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
|
|||||||
if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
|
if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
|
||||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||||
}
|
}
|
||||||
|
// Enable unified vertex attributes and query vertex buffer address when the driver supports it
|
||||||
|
if (device.HasVertexBufferUnifiedMemory()) {
|
||||||
|
glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
|
||||||
|
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
|
||||||
|
glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
|
||||||
|
glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
|
||||||
|
&vertex_buffer_address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererOpenGL::~RendererOpenGL() = default;
|
RendererOpenGL::~RendererOpenGL() = default;
|
||||||
@ -667,7 +675,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
|||||||
offsetof(ScreenRectVertex, tex_coord));
|
offsetof(ScreenRectVertex, tex_coord));
|
||||||
glVertexAttribBinding(PositionLocation, 0);
|
glVertexAttribBinding(PositionLocation, 0);
|
||||||
glVertexAttribBinding(TexCoordLocation, 0);
|
glVertexAttribBinding(TexCoordLocation, 0);
|
||||||
|
if (device.HasVertexBufferUnifiedMemory()) {
|
||||||
|
glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
|
||||||
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
|
||||||
|
sizeof(vertices));
|
||||||
|
} else {
|
||||||
glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
|
glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
|
||||||
|
}
|
||||||
|
|
||||||
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
|
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
|
||||||
glBindSampler(0, present_sampler.handle);
|
glBindSampler(0, present_sampler.handle);
|
||||||
|
@ -563,22 +563,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
|
|||||||
}
|
}
|
||||||
buffer_handles.push_back(handle);
|
buffer_handles.push_back(handle);
|
||||||
}
|
}
|
||||||
|
const u32 device_max = device.GetMaxVertexInputBindings();
|
||||||
|
const u32 min_binding = std::min(bindings.min_index, device_max);
|
||||||
|
const u32 max_binding = std::min(bindings.max_index, device_max);
|
||||||
|
const u32 binding_count = max_binding - min_binding;
|
||||||
|
if (binding_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||||
scheduler.Record([this, bindings_ = std::move(bindings),
|
scheduler.Record([bindings_ = std::move(bindings),
|
||||||
buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
|
buffer_handles_ = std::move(buffer_handles),
|
||||||
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
|
binding_count](vk::CommandBuffer cmdbuf) {
|
||||||
std::min(bindings_.max_index - bindings_.min_index,
|
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
|
||||||
device.GetMaxVertexInputBindings()),
|
bindings_.offsets.data(), bindings_.sizes.data(),
|
||||||
buffer_handles_.data(), bindings_.offsets.data(),
|
bindings_.strides.data());
|
||||||
bindings_.sizes.data(), bindings_.strides.data());
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
scheduler.Record([this, bindings_ = std::move(bindings),
|
scheduler.Record([bindings_ = std::move(bindings),
|
||||||
buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
|
buffer_handles_ = std::move(buffer_handles),
|
||||||
cmdbuf.BindVertexBuffers(bindings_.min_index,
|
binding_count](vk::CommandBuffer cmdbuf) {
|
||||||
std::min(bindings_.max_index - bindings_.min_index,
|
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
|
||||||
device.GetMaxVertexInputBindings()),
|
bindings_.offsets.data());
|
||||||
buffer_handles_.data(), bindings_.offsets.data());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
|||||||
.has_broken_robust =
|
.has_broken_robust =
|
||||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||||
|
.max_user_clip_distances = device.GetMaxUserClipDistances(),
|
||||||
};
|
};
|
||||||
|
|
||||||
host_info = Shader::HostTranslateInfo{
|
host_info = Shader::HostTranslateInfo{
|
||||||
|
@ -289,12 +289,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (has_multi_queries) {
|
if (has_multi_queries) {
|
||||||
size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
|
const size_t min_accumulation_limit =
|
||||||
|
std::min(first_accumulation_checkpoint, num_slots_used);
|
||||||
|
const size_t max_accumulation_limit =
|
||||||
|
std::max(last_accumulation_checkpoint, num_slots_used);
|
||||||
|
const size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
|
||||||
resolve_buffers.push_back(intermediary_buffer_index);
|
resolve_buffers.push_back(intermediary_buffer_index);
|
||||||
queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
|
queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
|
||||||
*buffers[resolve_buffer_index], num_slots_used,
|
*buffers[resolve_buffer_index], num_slots_used,
|
||||||
std::min(first_accumulation_checkpoint, num_slots_used),
|
min_accumulation_limit, max_accumulation_limit);
|
||||||
last_accumulation_checkpoint);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
@ -695,6 +695,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
std::min(properties.properties.limits.maxVertexInputBindings, 16U);
|
std::min(properties.properties.limits.maxVertexInputBindings, 16U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_turnip) {
|
||||||
|
LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
|
||||||
|
properties.properties.limits.maxVertexInputBindings = 32;
|
||||||
|
}
|
||||||
|
|
||||||
if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
|
if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
|
||||||
LOG_INFO(Render_Vulkan,
|
LOG_INFO(Render_Vulkan,
|
||||||
"Removing extendedDynamicState2 due to missing extendedDynamicState");
|
"Removing extendedDynamicState2 due to missing extendedDynamicState");
|
||||||
|
@ -665,6 +665,10 @@ public:
|
|||||||
return properties.properties.limits.maxViewports;
|
return properties.properties.limits.maxViewports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetMaxUserClipDistances() const {
|
||||||
|
return properties.properties.limits.maxClipDistances;
|
||||||
|
}
|
||||||
|
|
||||||
bool SupportsConditionalBarriers() const {
|
bool SupportsConditionalBarriers() const {
|
||||||
return supports_conditional_barriers;
|
return supports_conditional_barriers;
|
||||||
}
|
}
|
||||||
|
@ -193,8 +193,8 @@ void ConfigureUi::RequestGameListUpdate() {
|
|||||||
void ConfigureUi::SetConfiguration() {
|
void ConfigureUi::SetConfiguration() {
|
||||||
ui->theme_combobox->setCurrentIndex(
|
ui->theme_combobox->setCurrentIndex(
|
||||||
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
|
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
|
||||||
ui->language_combobox->setCurrentIndex(
|
ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
|
||||||
ui->language_combobox->findData(QString::fromStdString(UISettings::values.language)));
|
QString::fromStdString(UISettings::values.language.GetValue())));
|
||||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
||||||
ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
|
ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
|
||||||
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
|
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
|
||||||
|
@ -187,7 +187,6 @@ void QtConfig::ReadPathValues() {
|
|||||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||||
|
|
||||||
UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
|
UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
|
||||||
UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
|
|
||||||
UISettings::values.game_dir_deprecated =
|
UISettings::values.game_dir_deprecated =
|
||||||
ReadStringSetting(std::string("gameListRootDir"), std::string("."));
|
ReadStringSetting(std::string("gameListRootDir"), std::string("."));
|
||||||
UISettings::values.game_dir_deprecated_deepscan =
|
UISettings::values.game_dir_deprecated_deepscan =
|
||||||
@ -225,8 +224,8 @@ void QtConfig::ReadPathValues() {
|
|||||||
UISettings::values.recent_files =
|
UISettings::values.recent_files =
|
||||||
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
|
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
|
||||||
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
|
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
|
||||||
UISettings::values.language =
|
|
||||||
ReadStringSetting(std::string("language"), std::make_optional(std::string("")));
|
ReadCategory(Settings::Category::Paths);
|
||||||
|
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
@ -408,8 +407,9 @@ void QtConfig::SaveQtControlValues() {
|
|||||||
void QtConfig::SavePathValues() {
|
void QtConfig::SavePathValues() {
|
||||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Paths);
|
||||||
|
|
||||||
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
|
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
|
||||||
WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
|
|
||||||
BeginArray(std::string("gamedirs"));
|
BeginArray(std::string("gamedirs"));
|
||||||
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
|
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
|
||||||
SetArrayIndex(i);
|
SetArrayIndex(i);
|
||||||
@ -422,7 +422,6 @@ void QtConfig::SavePathValues() {
|
|||||||
|
|
||||||
WriteSetting(std::string("recentFiles"),
|
WriteSetting(std::string("recentFiles"),
|
||||||
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
|
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
|
||||||
WriteSetting(std::string("language"), UISettings::values.language);
|
|
||||||
|
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
|
@ -5147,12 +5147,12 @@ void GMainWindow::UpdateUITheme() {
|
|||||||
void GMainWindow::LoadTranslation() {
|
void GMainWindow::LoadTranslation() {
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
if (UISettings::values.language.empty()) {
|
if (UISettings::values.language.GetValue().empty()) {
|
||||||
// If the selected language is empty, use system locale
|
// If the selected language is empty, use system locale
|
||||||
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
|
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
|
||||||
} else {
|
} else {
|
||||||
// Otherwise load from the specified file
|
// Otherwise load from the specified file
|
||||||
loaded = translator.load(QString::fromStdString(UISettings::values.language),
|
loaded = translator.load(QString::fromStdString(UISettings::values.language.GetValue()),
|
||||||
QStringLiteral(":/languages/"));
|
QStringLiteral(":/languages/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5164,7 +5164,7 @@ void GMainWindow::LoadTranslation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnLanguageChanged(const QString& locale) {
|
void GMainWindow::OnLanguageChanged(const QString& locale) {
|
||||||
if (UISettings::values.language != std::string("en")) {
|
if (UISettings::values.language.GetValue() != std::string("en")) {
|
||||||
qApp->removeTranslator(&translator);
|
qApp->removeTranslator(&translator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,12 +154,11 @@ struct Values {
|
|||||||
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
||||||
|
|
||||||
std::string roms_path;
|
std::string roms_path;
|
||||||
std::string symbols_path;
|
|
||||||
std::string game_dir_deprecated;
|
std::string game_dir_deprecated;
|
||||||
bool game_dir_deprecated_deepscan;
|
bool game_dir_deprecated_deepscan;
|
||||||
QVector<GameDir> game_dirs;
|
QVector<GameDir> game_dirs;
|
||||||
QStringList recent_files;
|
QStringList recent_files;
|
||||||
std::string language;
|
Setting<std::string> language{linkage, {}, "language", Category::Paths};
|
||||||
|
|
||||||
std::string theme;
|
std::string theme;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user