Compare commits
48 Commits
android-16
...
android-16
Author | SHA1 | Date | |
---|---|---|---|
a801fe49b3 | |||
a54ce2571c | |||
426d4e4df5 | |||
3d268b8480 | |||
ad7445d4cc | |||
3a30271219 | |||
bb5196aaae | |||
d3070cafa7 | |||
5cd3b6f58c | |||
bedc758fe7 | |||
76701185ad | |||
f1cb14eb54 | |||
f4f4a469a9 | |||
9e5b4052ed | |||
234867b84d | |||
4b60aec190 | |||
ecfba79d98 | |||
310834aea2 | |||
6a1fa9bb17 | |||
1bb76201e6 | |||
372bca5945 | |||
93c19a40bf | |||
345ec25532 | |||
a94721fde0 | |||
816c7a8d1f | |||
efe52db690 | |||
d61df0f400 | |||
b14547b8b6 | |||
97ad3e7530 | |||
0589a32f75 | |||
617dc0f822 | |||
fcfa8b680b | |||
94244437de | |||
53956a2990 | |||
a7731abb72 | |||
50fd029eaa | |||
a2b567dfd6 | |||
b770f6a985 | |||
797e8fdbc3 | |||
65e646eeba | |||
fba3fa705d | |||
6ca530a721 | |||
e01c535178 | |||
7239547ead | |||
7fc06260d1 | |||
fcc85abe27 | |||
6851e93296 | |||
ffbba74c91 |
@ -305,7 +305,7 @@ find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(Vulkan 1.3.256 REQUIRED)
|
||||
find_package(Vulkan 1.3.274 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
|
@ -1,10 +1,7 @@
|
||||
| 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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [12410](https://github.com/yuzu-emu/yuzu//pull/12410) | [`d0a75580d`](https://github.com/yuzu-emu/yuzu//pull/12410/files) | renderer_vulkan: don't pass null view when nullDescriptor is not supported | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12432](https://github.com/yuzu-emu/yuzu//pull/12432) | [`9e9aed41b`](https://github.com/yuzu-emu/yuzu//pull/12432/files) | shader_recompiler: use float image operations on load/store when required | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: df60f03168...80207f9da8
@ -91,18 +91,20 @@ class GamesFragment : Fragment() {
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it }
|
||||
gamesViewModel.isReloading.collect {
|
||||
binding.swipeRefresh.isRefreshing = it
|
||||
if (gamesViewModel.games.value.isEmpty() && !it) {
|
||||
binding.noticeText.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.noticeText.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
gamesViewModel.games.collectLatest {
|
||||
(binding.gridGames.adapter as GameAdapter).submitList(it)
|
||||
if (it.isEmpty()) {
|
||||
binding.noticeText.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.noticeText.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
}
|
||||
|
||||
~OboeSinkStream() override {
|
||||
LOG_DEBUG(Audio_Sink, "Destructing Oboe stream {}", name);
|
||||
LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
|
||||
}
|
||||
|
||||
void Finalize() override {
|
||||
@ -66,11 +66,7 @@ public:
|
||||
std::shared_ptr<oboe::AudioStream> temp_stream;
|
||||
oboe::AudioStreamBuilder builder;
|
||||
|
||||
const auto result = builder.setDirection(direction)
|
||||
->setSampleRate(TargetSampleRate)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setFormatConversionAllowed(true)
|
||||
->openStream(temp_stream);
|
||||
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
|
||||
ASSERT(result == oboe::Result::OK);
|
||||
|
||||
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
||||
@ -105,6 +101,20 @@ protected:
|
||||
}
|
||||
|
||||
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() {
|
||||
const auto direction = [&]() {
|
||||
switch (type) {
|
||||
@ -135,12 +145,10 @@ private:
|
||||
}();
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
const auto result = builder.setDirection(direction)
|
||||
->setSampleRate(TargetSampleRate)
|
||||
const auto result = ConfigureBuilder(builder, direction)
|
||||
->setChannelCount(expected_channels)
|
||||
->setChannelMask(expected_mask)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setFormatConversionAllowed(true)
|
||||
->setChannelConversionAllowed(true)
|
||||
->setDataCallback(this)
|
||||
->setErrorCallback(this)
|
||||
->openStream(m_stream);
|
||||
@ -151,8 +159,16 @@ private:
|
||||
bool SetStreamProperties() {
|
||||
ASSERT(m_stream);
|
||||
|
||||
m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
|
||||
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;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||
}
|
||||
|
||||
using namespace Common::Literals;
|
||||
constexpr u32 StackSize = 32_KiB;
|
||||
constexpr u32 StackSize = 128_KiB;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -5,8 +5,6 @@
|
||||
#include "common/bit_cast.h"
|
||||
#include "core/arm/nce/interpreter_visitor.h"
|
||||
|
||||
#include <dynarmic/frontend/A64/decoder/a64.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
template <u32 BitSize>
|
||||
@ -249,6 +247,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Size in bytes
|
||||
const u64 size = 4 << opc.ZeroExtend();
|
||||
const u64 offset = imm19.SignExtend<u64>() << 2;
|
||||
const u64 address = this->GetPc() + offset;
|
||||
@ -530,7 +529,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u128 data{};
|
||||
m_memory.ReadBlock(address, &data, datasize);
|
||||
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||
this->SetVec(Vt, data);
|
||||
break;
|
||||
}
|
||||
|
@ -4,9 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
|
||||
#include <dynarmic/frontend/A64/a64_types.h>
|
||||
#include <dynarmic/frontend/A64/decoder/a64.h>
|
||||
#include <dynarmic/frontend/imm.h>
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace Core {
|
||||
|
||||
class VisitorBase {
|
||||
|
@ -54,6 +54,13 @@ enum class ImageDirectoryId : u32 {
|
||||
SdCard,
|
||||
};
|
||||
|
||||
enum class OpenDirectoryMode : u64 {
|
||||
Directory = (1 << 0),
|
||||
File = (1 << 1),
|
||||
All = Directory | File
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
|
||||
|
||||
class FileSystemController {
|
||||
public:
|
||||
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> {
|
||||
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_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDirectory::Read, "Read"},
|
||||
@ -269,8 +269,12 @@ public:
|
||||
|
||||
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||
// Build entry index now to save time later.
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
|
||||
if (True(mode & OpenDirectoryMode::Directory)) {
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
|
||||
}
|
||||
if (True(mode & OpenDirectoryMode::File)) {
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -446,11 +450,9 @@ public:
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const auto mode = rp.PopRaw<OpenDirectoryMode>();
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
const u32 filter_flags = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
|
||||
|
||||
FileSys::VirtualDir vfs_dir{};
|
||||
auto result = backend.OpenDirectory(&vfs_dir, name);
|
||||
@ -460,7 +462,7 @@ public:
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -51,7 +51,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
||||
|
||||
if (resource_manager != nullptr) {
|
||||
if (resource_manager != nullptr && resource_manager->GetNpad()) {
|
||||
resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,11 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
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)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
||||
}
|
||||
|
@ -214,16 +214,16 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
|
||||
}
|
||||
}
|
||||
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
|
||||
return ctx.OpLoad(def.image_type, def.id);
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
|
||||
return ctx.OpLoad(def.image_type, def.id);
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,13 +566,23 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
|
||||
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
|
||||
return ctx.ConstantNull(ctx.U32[4]);
|
||||
}
|
||||
return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
|
||||
Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
|
||||
const auto [image, is_integer] = Image(ctx, index, info);
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
|
||||
result_type, image, coords, std::nullopt, std::span<const Id>{})};
|
||||
if (!is_integer) {
|
||||
color = ctx.OpBitcast(ctx.U32[4], color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
ctx.OpImageWrite(Image(ctx, index, info), coords, color);
|
||||
const auto [image, is_integer] = Image(ctx, index, info);
|
||||
if (!is_integer) {
|
||||
color = ctx.OpBitcast(ctx.F32[4], color);
|
||||
}
|
||||
ctx.OpImageWrite(image, coords, color);
|
||||
}
|
||||
|
||||
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
|
||||
|
@ -74,20 +74,19 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
|
||||
throw InvalidArgument("Invalid image format {}", format);
|
||||
}
|
||||
|
||||
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
|
||||
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
|
||||
const spv::ImageFormat format{GetImageFormat(desc.format)};
|
||||
const Id type{ctx.U32[1]};
|
||||
switch (desc.type) {
|
||||
case TextureType::Color1D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
|
||||
case TextureType::ColorArray1D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
|
||||
case TextureType::Color2D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
|
||||
case TextureType::ColorArray2D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format);
|
||||
case TextureType::Color3D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
|
||||
case TextureType::Buffer:
|
||||
throw NotImplementedException("Image buffer");
|
||||
default:
|
||||
@ -1273,7 +1272,9 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
||||
throw NotImplementedException("Array of image buffers");
|
||||
}
|
||||
const spv::ImageFormat format{GetImageFormat(desc.format)};
|
||||
const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
|
||||
const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
|
||||
const Id image_type{
|
||||
TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
|
||||
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
|
||||
Decorate(id, spv::Decoration::Binding, binding);
|
||||
@ -1283,6 +1284,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
||||
.id = id,
|
||||
.image_type = image_type,
|
||||
.count = desc.count,
|
||||
.is_integer = desc.is_integer,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
@ -1327,7 +1329,8 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Array of images");
|
||||
}
|
||||
const Id image_type{ImageType(*this, desc)};
|
||||
const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
|
||||
const Id image_type{ImageType(*this, desc, sampled_type)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
|
||||
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
|
||||
Decorate(id, spv::Decoration::Binding, binding);
|
||||
@ -1337,6 +1340,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
|
||||
.id = id,
|
||||
.image_type = image_type,
|
||||
.count = desc.count,
|
||||
.is_integer = desc.is_integer,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
@ -1528,7 +1532,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
if (stage == Stage::Fragment) {
|
||||
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);
|
||||
}
|
||||
if (info.stores[IR::Attribute::Layer] &&
|
||||
|
@ -47,12 +47,14 @@ struct ImageBufferDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
bool is_integer;
|
||||
};
|
||||
|
||||
struct ImageDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
bool is_integer;
|
||||
};
|
||||
|
||||
struct UniformDefinitions {
|
||||
|
@ -24,6 +24,8 @@ public:
|
||||
|
||||
[[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
|
||||
|
||||
[[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
|
||||
|
@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
|
||||
}
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
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,
|
||||
header.vtg.omap_systemb.primitive_array_id != 0);
|
||||
|
@ -372,6 +372,10 @@ TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAdd
|
||||
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
class Descriptors {
|
||||
public:
|
||||
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
|
||||
@ -403,6 +407,7 @@ public:
|
||||
})};
|
||||
image_buffer_descriptors[index].is_written |= desc.is_written;
|
||||
image_buffer_descriptors[index].is_read |= desc.is_read;
|
||||
image_buffer_descriptors[index].is_integer |= desc.is_integer;
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -432,6 +437,7 @@ public:
|
||||
})};
|
||||
image_descriptors[index].is_written |= desc.is_written;
|
||||
image_descriptors[index].is_read |= desc.is_read;
|
||||
image_descriptors[index].is_integer |= desc.is_integer;
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -469,6 +475,20 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
|
||||
ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
|
||||
}
|
||||
|
||||
bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
|
||||
switch (pixel_format) {
|
||||
case TexturePixelFormat::A8B8G8R8_SNORM:
|
||||
case TexturePixelFormat::R8G8_SNORM:
|
||||
case TexturePixelFormat::R8_SNORM:
|
||||
case TexturePixelFormat::R16G16B16A16_SNORM:
|
||||
case TexturePixelFormat::R16G16_SNORM:
|
||||
case TexturePixelFormat::R16_SNORM:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
|
||||
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
@ -587,11 +607,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
}
|
||||
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
||||
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
||||
const bool is_integer{IsTexturePixelFormatInteger(env, cbuf)};
|
||||
if (flags.type == TextureType::Buffer) {
|
||||
index = descriptors.Add(ImageBufferDescriptor{
|
||||
.format = flags.image_format,
|
||||
.is_written = is_written,
|
||||
.is_read = is_read,
|
||||
.is_integer = is_integer,
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
.count = cbuf.count,
|
||||
@ -603,6 +625,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
.format = flags.image_format,
|
||||
.is_written = is_written,
|
||||
.is_read = is_read,
|
||||
.is_integer = is_integer,
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
.count = cbuf.count,
|
||||
@ -658,7 +681,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
|
||||
flags.type == TextureType::Buffer) {
|
||||
const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
|
||||
if (pixel_format != TexturePixelFormat::OTHER) {
|
||||
if (IsPixelFormatSNorm(pixel_format)) {
|
||||
PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,8 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
@ -35,14 +35,109 @@ enum class TextureType : u32 {
|
||||
};
|
||||
constexpr u32 NUM_TEXTURE_TYPES = 9;
|
||||
|
||||
enum class TexturePixelFormat : u32 {
|
||||
enum class TexturePixelFormat {
|
||||
A8B8G8R8_UNORM,
|
||||
A8B8G8R8_SNORM,
|
||||
A8B8G8R8_SINT,
|
||||
A8B8G8R8_UINT,
|
||||
R5G6B5_UNORM,
|
||||
B5G6R5_UNORM,
|
||||
A1R5G5B5_UNORM,
|
||||
A2B10G10R10_UNORM,
|
||||
A2B10G10R10_UINT,
|
||||
A2R10G10B10_UNORM,
|
||||
A1B5G5R5_UNORM,
|
||||
A5B5G5R1_UNORM,
|
||||
R8_UNORM,
|
||||
R8_SNORM,
|
||||
R8G8_SNORM,
|
||||
R8_SINT,
|
||||
R8_UINT,
|
||||
R16G16B16A16_FLOAT,
|
||||
R16G16B16A16_UNORM,
|
||||
R16G16B16A16_SNORM,
|
||||
R16G16_SNORM,
|
||||
R16G16B16A16_SINT,
|
||||
R16G16B16A16_UINT,
|
||||
B10G11R11_FLOAT,
|
||||
R32G32B32A32_UINT,
|
||||
BC1_RGBA_UNORM,
|
||||
BC2_UNORM,
|
||||
BC3_UNORM,
|
||||
BC4_UNORM,
|
||||
BC4_SNORM,
|
||||
BC5_UNORM,
|
||||
BC5_SNORM,
|
||||
BC7_UNORM,
|
||||
BC6H_UFLOAT,
|
||||
BC6H_SFLOAT,
|
||||
ASTC_2D_4X4_UNORM,
|
||||
B8G8R8A8_UNORM,
|
||||
R32G32B32A32_FLOAT,
|
||||
R32G32B32A32_SINT,
|
||||
R32G32_FLOAT,
|
||||
R32G32_SINT,
|
||||
R32_FLOAT,
|
||||
R16_FLOAT,
|
||||
R16_UNORM,
|
||||
R16_SNORM,
|
||||
OTHER
|
||||
R16_UINT,
|
||||
R16_SINT,
|
||||
R16G16_UNORM,
|
||||
R16G16_FLOAT,
|
||||
R16G16_UINT,
|
||||
R16G16_SINT,
|
||||
R16G16_SNORM,
|
||||
R32G32B32_FLOAT,
|
||||
A8B8G8R8_SRGB,
|
||||
R8G8_UNORM,
|
||||
R8G8_SNORM,
|
||||
R8G8_SINT,
|
||||
R8G8_UINT,
|
||||
R32G32_UINT,
|
||||
R16G16B16X16_FLOAT,
|
||||
R32_UINT,
|
||||
R32_SINT,
|
||||
ASTC_2D_8X8_UNORM,
|
||||
ASTC_2D_8X5_UNORM,
|
||||
ASTC_2D_5X4_UNORM,
|
||||
B8G8R8A8_SRGB,
|
||||
BC1_RGBA_SRGB,
|
||||
BC2_SRGB,
|
||||
BC3_SRGB,
|
||||
BC7_SRGB,
|
||||
A4B4G4R4_UNORM,
|
||||
G4R4_UNORM,
|
||||
ASTC_2D_4X4_SRGB,
|
||||
ASTC_2D_8X8_SRGB,
|
||||
ASTC_2D_8X5_SRGB,
|
||||
ASTC_2D_5X4_SRGB,
|
||||
ASTC_2D_5X5_UNORM,
|
||||
ASTC_2D_5X5_SRGB,
|
||||
ASTC_2D_10X8_UNORM,
|
||||
ASTC_2D_10X8_SRGB,
|
||||
ASTC_2D_6X6_UNORM,
|
||||
ASTC_2D_6X6_SRGB,
|
||||
ASTC_2D_10X6_UNORM,
|
||||
ASTC_2D_10X6_SRGB,
|
||||
ASTC_2D_10X5_UNORM,
|
||||
ASTC_2D_10X5_SRGB,
|
||||
ASTC_2D_10X10_UNORM,
|
||||
ASTC_2D_10X10_SRGB,
|
||||
ASTC_2D_12X10_UNORM,
|
||||
ASTC_2D_12X10_SRGB,
|
||||
ASTC_2D_12X12_UNORM,
|
||||
ASTC_2D_12X12_SRGB,
|
||||
ASTC_2D_8X6_UNORM,
|
||||
ASTC_2D_8X6_SRGB,
|
||||
ASTC_2D_6X5_UNORM,
|
||||
ASTC_2D_6X5_SRGB,
|
||||
E5B9G9R9_FLOAT,
|
||||
D32_FLOAT,
|
||||
D16_UNORM,
|
||||
X8_D24_UNORM,
|
||||
S8_UINT,
|
||||
D24_UNORM_S8_UINT,
|
||||
S8_UINT_D24_UNORM,
|
||||
D32_FLOAT_S8_UINT,
|
||||
};
|
||||
|
||||
enum class ImageFormat : u32 {
|
||||
@ -97,6 +192,7 @@ struct ImageBufferDescriptor {
|
||||
ImageFormat format;
|
||||
bool is_written;
|
||||
bool is_read;
|
||||
bool is_integer;
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
u32 count;
|
||||
@ -129,6 +225,7 @@ struct ImageDescriptor {
|
||||
ImageFormat format;
|
||||
bool is_written;
|
||||
bool is_read;
|
||||
bool is_integer;
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
u32 count;
|
||||
@ -227,6 +324,8 @@ struct Info {
|
||||
bool requires_layer_emulation{};
|
||||
IR::Attribute emulated_layer{};
|
||||
|
||||
u32 used_clip_distances{};
|
||||
|
||||
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
|
||||
constant_buffer_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());
|
||||
}
|
||||
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 {
|
||||
@ -109,6 +112,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_,
|
||||
: device{device_}, staging_buffer_pool{staging_buffer_pool_},
|
||||
has_fast_buffer_sub_data{device.HasFastBufferSubData()},
|
||||
use_assembly_shaders{device.UseAssemblyShaders()},
|
||||
has_unified_vertex_buffers{device.HasVertexBufferUnifiedMemory()},
|
||||
stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} {
|
||||
GLint 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) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
|
||||
index_buffer_offset = offset;
|
||||
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());
|
||||
index_buffer_offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
||||
static_cast<GLsizei>(stride));
|
||||
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),
|
||||
static_cast<GLsizei>(stride));
|
||||
}
|
||||
}
|
||||
|
||||
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); });
|
||||
const u32 count =
|
||||
std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index);
|
||||
glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
|
||||
reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
|
||||
buffer_strides.data());
|
||||
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(),
|
||||
reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
|
||||
buffer_strides.data());
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
|
||||
|
@ -209,6 +209,7 @@ private:
|
||||
|
||||
bool has_fast_buffer_sub_data = false;
|
||||
bool use_assembly_shaders = false;
|
||||
bool has_unified_vertex_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_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
|
||||
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_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
|
||||
has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough;
|
||||
|
@ -72,6 +72,10 @@ public:
|
||||
return has_texture_shadow_lod;
|
||||
}
|
||||
|
||||
bool HasVertexBufferUnifiedMemory() const {
|
||||
return has_vertex_buffer_unified_memory;
|
||||
}
|
||||
|
||||
bool HasASTC() const {
|
||||
return has_astc;
|
||||
}
|
||||
@ -211,6 +215,7 @@ private:
|
||||
bool has_vertex_viewport_layer{};
|
||||
bool has_image_load_formatted{};
|
||||
bool has_texture_shadow_lod{};
|
||||
bool has_vertex_buffer_unified_memory{};
|
||||
bool has_astc{};
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
|
@ -162,14 +162,18 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
|
||||
SyncFramebufferSRGB();
|
||||
}
|
||||
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;
|
||||
|
||||
state_tracker.NotifyDepthMask();
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1294,15 +1298,13 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum
|
||||
program->ConfigureTransformFeedback();
|
||||
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
||||
UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
|
||||
|
||||
// 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
|
||||
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
|
||||
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
|
||||
glBeginTransformFeedback(GL_POINTS);
|
||||
glBeginTransformFeedback(primitive_mode);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::EndTransformFeedback() {
|
||||
|
@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
|
||||
using VideoCommon::SerializePipeline;
|
||||
using Context = ShaderContext::Context;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 9;
|
||||
constexpr u32 CACHE_VERSION = 10;
|
||||
|
||||
template <typename Container>
|
||||
auto MakeSpan(Container& container) {
|
||||
@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.ignore_nan_fp_comparisons = true,
|
||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
||||
.max_user_clip_distances = 8,
|
||||
},
|
||||
host_info{
|
||||
.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) {
|
||||
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;
|
||||
@ -667,7 +675,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
offsetof(ScreenRectVertex, tex_coord));
|
||||
glVertexAttribBinding(PositionLocation, 0);
|
||||
glVertexAttribBinding(TexCoordLocation, 0);
|
||||
glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
|
||||
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));
|
||||
}
|
||||
|
||||
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
|
||||
glBindSampler(0, present_sampler.handle);
|
||||
|
@ -78,8 +78,15 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
|
||||
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
|
||||
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {
|
||||
if (runtime.device.HasNullDescriptor()) {
|
||||
return;
|
||||
}
|
||||
device = &runtime.device;
|
||||
buffer = runtime.CreateNullBuffer();
|
||||
is_null = true;
|
||||
}
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
|
||||
VAddr cpu_addr_, u64 size_bytes_)
|
||||
@ -93,8 +100,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
|
||||
|
||||
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
|
||||
if (!device) {
|
||||
// Null buffer, return a null descriptor
|
||||
// Null buffer supported, return a null descriptor
|
||||
return VK_NULL_HANDLE;
|
||||
} else if (is_null) {
|
||||
// Null buffer not supported, adjust offset and size
|
||||
offset = 0;
|
||||
size = 0;
|
||||
}
|
||||
const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
|
||||
return offset == view.offset && size == view.size && format == view.format;
|
||||
@ -563,22 +574,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
|
||||
}
|
||||
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()) {
|
||||
scheduler.Record([this, bindings_ = std::move(bindings),
|
||||
buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
|
||||
std::min(bindings_.max_index - bindings_.min_index,
|
||||
device.GetMaxVertexInputBindings()),
|
||||
buffer_handles_.data(), bindings_.offsets.data(),
|
||||
bindings_.sizes.data(), bindings_.strides.data());
|
||||
scheduler.Record([bindings_ = std::move(bindings),
|
||||
buffer_handles_ = std::move(buffer_handles),
|
||||
binding_count](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
|
||||
bindings_.offsets.data(), bindings_.sizes.data(),
|
||||
bindings_.strides.data());
|
||||
});
|
||||
} else {
|
||||
scheduler.Record([this, bindings_ = std::move(bindings),
|
||||
buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers(bindings_.min_index,
|
||||
std::min(bindings_.max_index - bindings_.min_index,
|
||||
device.GetMaxVertexInputBindings()),
|
||||
buffer_handles_.data(), bindings_.offsets.data());
|
||||
scheduler.Record([bindings_ = std::move(bindings),
|
||||
buffer_handles_ = std::move(buffer_handles),
|
||||
binding_count](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
|
||||
bindings_.offsets.data());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -622,9 +638,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
if (null_buffer) {
|
||||
return;
|
||||
if (!null_buffer) {
|
||||
null_buffer = CreateNullBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
|
||||
VkBufferCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@ -639,15 +658,17 @@ void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
if (device.IsExtTransformFeedbackSupported()) {
|
||||
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
|
||||
}
|
||||
null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
|
||||
vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
null_buffer.SetObjectNameEXT("Null buffer");
|
||||
ret.SetObjectNameEXT("Null buffer");
|
||||
}
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -63,6 +63,7 @@ private:
|
||||
vk::Buffer buffer;
|
||||
std::vector<BufferView> views;
|
||||
VideoCommon::UsageTracker tracker;
|
||||
bool is_null{};
|
||||
};
|
||||
|
||||
class QuadArrayIndexBuffer;
|
||||
@ -151,6 +152,7 @@ private:
|
||||
}
|
||||
|
||||
void ReserveNullBuffer();
|
||||
vk::Buffer CreateNullBuffer();
|
||||
|
||||
const Device& device;
|
||||
MemoryAllocator& memory_allocator;
|
||||
|
@ -54,7 +54,7 @@ using VideoCommon::FileEnvironment;
|
||||
using VideoCommon::GenericEnvironment;
|
||||
using VideoCommon::GraphicsEnvironment;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 10;
|
||||
constexpr u32 CACHE_VERSION = 11;
|
||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||
|
||||
template <typename Container>
|
||||
@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.has_broken_robust =
|
||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_user_clip_distances = device.GetMaxUserClipDistances(),
|
||||
};
|
||||
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
|
@ -289,12 +289,15 @@ public:
|
||||
}
|
||||
|
||||
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);
|
||||
queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
|
||||
*buffers[resolve_buffer_index], num_slots_used,
|
||||
std::min(first_accumulation_checkpoint, num_slots_used),
|
||||
last_accumulation_checkpoint);
|
||||
min_accumulation_limit, max_accumulation_limit);
|
||||
|
||||
} else {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
@ -62,23 +62,9 @@ static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& en
|
||||
}
|
||||
|
||||
static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
|
||||
switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
|
||||
entry.a_type, entry.srgb_conversion)) {
|
||||
case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM:
|
||||
return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
|
||||
case VideoCore::Surface::PixelFormat::R8_SNORM:
|
||||
return Shader::TexturePixelFormat::R8_SNORM;
|
||||
case VideoCore::Surface::PixelFormat::R8G8_SNORM:
|
||||
return Shader::TexturePixelFormat::R8G8_SNORM;
|
||||
case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
|
||||
return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
|
||||
case VideoCore::Surface::PixelFormat::R16G16_SNORM:
|
||||
return Shader::TexturePixelFormat::R16G16_SNORM;
|
||||
case VideoCore::Surface::PixelFormat::R16_SNORM:
|
||||
return Shader::TexturePixelFormat::R16_SNORM;
|
||||
default:
|
||||
return Shader::TexturePixelFormat::OTHER;
|
||||
}
|
||||
return static_cast<Shader::TexturePixelFormat>(
|
||||
PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
|
||||
entry.a_type, entry.srgb_conversion));
|
||||
}
|
||||
|
||||
static std::string_view StageToPrefix(Shader::Stage stage) {
|
||||
@ -398,6 +384,11 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GraphicsEnvironment::IsTexturePixelFormatInteger(u32 handle) {
|
||||
return VideoCore::Surface::IsPixelFormatInteger(
|
||||
static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
|
||||
}
|
||||
|
||||
u32 GraphicsEnvironment::ReadViewportTransformState() {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
viewport_transform_state = regs.viewport_scale_offset_enabled;
|
||||
@ -448,6 +439,11 @@ Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ComputeEnvironment::IsTexturePixelFormatInteger(u32 handle) {
|
||||
return VideoCore::Surface::IsPixelFormatInteger(
|
||||
static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
|
||||
}
|
||||
|
||||
u32 ComputeEnvironment::ReadViewportTransformState() {
|
||||
return viewport_transform_state;
|
||||
}
|
||||
@ -551,6 +547,11 @@ Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool FileEnvironment::IsTexturePixelFormatInteger(u32 handle) {
|
||||
return VideoCore::Surface::IsPixelFormatInteger(
|
||||
static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
|
||||
}
|
||||
|
||||
u32 FileEnvironment::ReadViewportTransformState() {
|
||||
return viewport_transform_state;
|
||||
}
|
||||
|
@ -115,6 +115,8 @@ public:
|
||||
|
||||
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
|
||||
u32 ReadViewportTransformState() override;
|
||||
|
||||
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
|
||||
@ -139,6 +141,8 @@ public:
|
||||
|
||||
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
|
||||
u32 ReadViewportTransformState() override;
|
||||
|
||||
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
|
||||
@ -171,6 +175,8 @@ public:
|
||||
|
||||
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
[[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
|
||||
[[nodiscard]] u32 ReadViewportTransformState() override;
|
||||
|
||||
[[nodiscard]] u32 LocalMemorySize() const override;
|
||||
|
@ -695,6 +695,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
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) {
|
||||
LOG_INFO(Render_Vulkan,
|
||||
"Removing extendedDynamicState2 due to missing extendedDynamicState");
|
||||
|
@ -665,6 +665,10 @@ public:
|
||||
return properties.properties.limits.maxViewports;
|
||||
}
|
||||
|
||||
u32 GetMaxUserClipDistances() const {
|
||||
return properties.properties.limits.maxClipDistances;
|
||||
}
|
||||
|
||||
bool SupportsConditionalBarriers() const {
|
||||
return supports_conditional_barriers;
|
||||
}
|
||||
|
@ -377,6 +377,8 @@ const char* ToString(VkResult result) noexcept {
|
||||
return "VK_OPERATION_DEFERRED_KHR";
|
||||
case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
|
||||
return "VK_OPERATION_NOT_DEFERRED_KHR";
|
||||
case VkResult::VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR:
|
||||
return "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
|
||||
case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
|
||||
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
|
||||
case VkResult::VK_RESULT_MAX_ENUM:
|
||||
|
@ -106,32 +106,30 @@ ConfigureGraphics::ConfigureGraphics(
|
||||
Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue()));
|
||||
UpdateAPILayout();
|
||||
PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
|
||||
PopulateVSyncModeSelection(false); //< must happen after UpdateAPILayout
|
||||
|
||||
// VSync setting needs to be determined after populating the VSync combobox
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
|
||||
const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
|
||||
int index{};
|
||||
for (const auto mode : vsync_mode_combobox_enum_map) {
|
||||
if (mode == vsync_mode) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
|
||||
vsync_mode_combobox->setCurrentIndex(index);
|
||||
const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
|
||||
const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
|
||||
int index{};
|
||||
for (const auto mode : vsync_mode_combobox_enum_map) {
|
||||
if (mode == vsync_mode) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
|
||||
vsync_mode_combobox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
|
||||
UpdateAPILayout();
|
||||
PopulateVSyncModeSelection();
|
||||
PopulateVSyncModeSelection(false);
|
||||
});
|
||||
connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
|
||||
[this](int device) {
|
||||
UpdateDeviceSelection(device);
|
||||
PopulateVSyncModeSelection();
|
||||
PopulateVSyncModeSelection(false);
|
||||
});
|
||||
connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
|
||||
[this](int backend) { UpdateShaderBackendSelection(backend); });
|
||||
@ -147,8 +145,9 @@ ConfigureGraphics::ConfigureGraphics(
|
||||
const auto& update_screenshot_info = [this, &builder]() {
|
||||
const auto& combobox_enumerations = builder.ComboboxTranslations().at(
|
||||
Settings::EnumMetadata<Settings::AspectRatio>::Index());
|
||||
const auto index = aspect_ratio_combobox->currentIndex();
|
||||
const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first);
|
||||
const auto ratio_index = aspect_ratio_combobox->currentIndex();
|
||||
const auto ratio =
|
||||
static_cast<Settings::AspectRatio>(combobox_enumerations[ratio_index].first);
|
||||
|
||||
const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
|
||||
Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
|
||||
@ -174,11 +173,7 @@ ConfigureGraphics::ConfigureGraphics(
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::PopulateVSyncModeSelection() {
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void ConfigureGraphics::PopulateVSyncModeSelection(bool use_setting) {
|
||||
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
|
||||
if (backend == Settings::RendererBackend::Null) {
|
||||
vsync_mode_combobox->setEnabled(false);
|
||||
@ -189,8 +184,9 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
|
||||
const int current_index = //< current selected vsync mode from combobox
|
||||
vsync_mode_combobox->currentIndex();
|
||||
const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
|
||||
current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
|
||||
: vsync_mode_combobox_enum_map[current_index];
|
||||
current_index == -1 || use_setting
|
||||
? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
|
||||
: vsync_mode_combobox_enum_map[current_index];
|
||||
int index{};
|
||||
const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
|
||||
|
||||
@ -214,6 +210,23 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
vsync_restore_global_button->setVisible(!Settings::values.vsync_mode.UsingGlobal());
|
||||
|
||||
const Settings::VSyncMode global_vsync_mode = Settings::values.vsync_mode.GetValue(true);
|
||||
vsync_restore_global_button->setEnabled(
|
||||
(backend == Settings::RendererBackend::OpenGL &&
|
||||
(global_vsync_mode == Settings::VSyncMode::Immediate ||
|
||||
global_vsync_mode == Settings::VSyncMode::Fifo)) ||
|
||||
backend == Settings::RendererBackend::Vulkan);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::UpdateVsyncSetting() const {
|
||||
const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
|
||||
const auto vsync_mode = PresentModeToSetting(mode);
|
||||
Settings::values.vsync_mode.SetValue(vsync_mode);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::UpdateDeviceSelection(int device) {
|
||||
@ -299,6 +312,33 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
} else if (setting->Id() == Settings::values.vsync_mode.Id()) {
|
||||
// Keep track of vsync_mode's combobox so we can populate it
|
||||
vsync_mode_combobox = widget->combobox;
|
||||
|
||||
// Since vsync is populated at runtime, we have to manually set up the button for
|
||||
// restoring the global setting.
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QPushButton* restore_button =
|
||||
ConfigurationShared::Widget::CreateRestoreGlobalButton(
|
||||
Settings::values.vsync_mode.UsingGlobal(), widget);
|
||||
restore_button->setEnabled(true);
|
||||
widget->layout()->addWidget(restore_button);
|
||||
|
||||
QObject::connect(restore_button, &QAbstractButton::clicked,
|
||||
[restore_button, this](bool) {
|
||||
Settings::values.vsync_mode.SetGlobal(true);
|
||||
PopulateVSyncModeSelection(true);
|
||||
|
||||
restore_button->setVisible(false);
|
||||
});
|
||||
|
||||
std::function<void()> set_non_global = [restore_button, this]() {
|
||||
Settings::values.vsync_mode.SetGlobal(false);
|
||||
UpdateVsyncSetting();
|
||||
restore_button->setVisible(true);
|
||||
};
|
||||
QObject::connect(widget->combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[set_non_global]() { set_non_global(); });
|
||||
vsync_restore_global_button = restore_button;
|
||||
}
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
} else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
|
||||
// Keep track of the aspect ratio combobox to update other UI tabs that need it
|
||||
@ -400,11 +440,7 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
func(powered_on);
|
||||
}
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
|
||||
const auto vsync_mode = PresentModeToSetting(mode);
|
||||
Settings::values.vsync_mode.SetValue(vsync_mode);
|
||||
}
|
||||
UpdateVsyncSetting();
|
||||
|
||||
Settings::values.vulkan_device.SetGlobal(true);
|
||||
Settings::values.shader_backend.SetGlobal(true);
|
||||
|
@ -62,7 +62,8 @@ private:
|
||||
|
||||
void Setup(const ConfigurationShared::Builder& builder);
|
||||
|
||||
void PopulateVSyncModeSelection();
|
||||
void PopulateVSyncModeSelection(bool use_setting);
|
||||
void UpdateVsyncSetting() const;
|
||||
void UpdateBackgroundColorButton(QColor color);
|
||||
void UpdateAPILayout();
|
||||
void UpdateDeviceSelection(int device);
|
||||
@ -104,6 +105,7 @@ private:
|
||||
QComboBox* api_combobox;
|
||||
QComboBox* shader_backend_combobox;
|
||||
QComboBox* vsync_mode_combobox;
|
||||
QPushButton* vsync_restore_global_button;
|
||||
QWidget* vulkan_device_widget;
|
||||
QWidget* api_widget;
|
||||
QWidget* shader_backend_widget;
|
||||
|
@ -193,8 +193,8 @@ void ConfigureUi::RequestGameListUpdate() {
|
||||
void ConfigureUi::SetConfiguration() {
|
||||
ui->theme_combobox->setCurrentIndex(
|
||||
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
|
||||
ui->language_combobox->setCurrentIndex(
|
||||
ui->language_combobox->findData(QString::fromStdString(UISettings::values.language)));
|
||||
ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
|
||||
QString::fromStdString(UISettings::values.language.GetValue())));
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
||||
ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
|
||||
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
|
||||
|
@ -187,7 +187,6 @@ void QtConfig::ReadPathValues() {
|
||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||
|
||||
UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
|
||||
UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
|
||||
UISettings::values.game_dir_deprecated =
|
||||
ReadStringSetting(std::string("gameListRootDir"), std::string("."));
|
||||
UISettings::values.game_dir_deprecated_deepscan =
|
||||
@ -225,8 +224,8 @@ void QtConfig::ReadPathValues() {
|
||||
UISettings::values.recent_files =
|
||||
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
|
||||
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
|
||||
UISettings::values.language =
|
||||
ReadStringSetting(std::string("language"), std::make_optional(std::string("")));
|
||||
|
||||
ReadCategory(Settings::Category::Paths);
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
@ -408,8 +407,9 @@ void QtConfig::SaveQtControlValues() {
|
||||
void QtConfig::SavePathValues() {
|
||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||
|
||||
WriteCategory(Settings::Category::Paths);
|
||||
|
||||
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
|
||||
WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
|
||||
BeginArray(std::string("gamedirs"));
|
||||
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
|
||||
SetArrayIndex(i);
|
||||
@ -422,7 +422,6 @@ void QtConfig::SavePathValues() {
|
||||
|
||||
WriteSetting(std::string("recentFiles"),
|
||||
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
|
||||
WriteSetting(std::string("language"), UISettings::values.language);
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
|
@ -5147,12 +5147,12 @@ void GMainWindow::UpdateUITheme() {
|
||||
void GMainWindow::LoadTranslation() {
|
||||
bool loaded;
|
||||
|
||||
if (UISettings::values.language.empty()) {
|
||||
if (UISettings::values.language.GetValue().empty()) {
|
||||
// If the selected language is empty, use system locale
|
||||
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
|
||||
} else {
|
||||
// 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/"));
|
||||
}
|
||||
|
||||
@ -5164,7 +5164,7 @@ void GMainWindow::LoadTranslation() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnLanguageChanged(const QString& locale) {
|
||||
if (UISettings::values.language != std::string("en")) {
|
||||
if (UISettings::values.language.GetValue() != std::string("en")) {
|
||||
qApp->removeTranslator(&translator);
|
||||
}
|
||||
|
||||
|
@ -154,12 +154,11 @@ struct Values {
|
||||
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
||||
|
||||
std::string roms_path;
|
||||
std::string symbols_path;
|
||||
std::string game_dir_deprecated;
|
||||
bool game_dir_deprecated_deepscan;
|
||||
QVector<GameDir> game_dirs;
|
||||
QStringList recent_files;
|
||||
std::string language;
|
||||
Setting<std::string> language{linkage, {}, "language", Category::Paths};
|
||||
|
||||
std::string theme;
|
||||
|
||||
|
Reference in New Issue
Block a user