early-access version 3971

This commit is contained in:
pineappleEA 2023-11-11 17:36:08 +01:00
parent b1b49d9789
commit 44caed8bb2
18 changed files with 88 additions and 62 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3970. This is the source code for early-access 3971.
## Legal Notice ## Legal Notice

View file

@ -203,6 +203,8 @@ const char* TranslateCategory(Category category) {
case Category::Ui: case Category::Ui:
case Category::UiGeneral: case Category::UiGeneral:
return "UI"; return "UI";
case Category::UiAudio:
return "UiAudio";
case Category::UiLayout: case Category::UiLayout:
return "UiLayout"; return "UiLayout";
case Category::UiGameList: case Category::UiGameList:

View file

@ -153,7 +153,7 @@ struct Values {
true, true,
true}; true};
Setting<bool, false> audio_muted{ Setting<bool, false> audio_muted{
linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
Setting<bool, false> dump_audio_commands{ Setting<bool, false> dump_audio_commands{
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};

View file

@ -32,6 +32,7 @@ enum class Category : u32 {
AddOns, AddOns,
Controls, Controls,
Ui, Ui,
UiAudio,
UiGeneral, UiGeneral,
UiLayout, UiLayout,
UiGameList, UiGameList,

View file

@ -68,10 +68,6 @@ public:
} }
}; };
void InvalidateEntireInstructionCache(Core::System& system) {
system.InvalidateCpuInstructionCaches();
}
template <typename AddressType> template <typename AddressType>
void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) { void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) {
system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size); system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
@ -435,9 +431,6 @@ void KPageTableBase::Finalize() {
m_mapped_ipc_server_memory); m_mapped_ipc_server_memory);
} }
// Invalidate the entire instruction cache.
InvalidateEntireInstructionCache(m_system);
// Close the backing page table, as the destructor is not called for guest objects. // Close the backing page table, as the destructor is not called for guest objects.
m_impl.reset(); m_impl.reset();
} }

View file

@ -3,16 +3,18 @@
#include <queue> #include <queue>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h" #include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS { namespace Service::IRS {
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, ClusteringProcessor::ClusteringProcessor(Core::System& system_,
Core::IrSensor::DeviceFormat& device_format, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index) std::size_t npad_index)
: device{device_format} { : device{device_format}, system{system_} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
} }
next_state.sampling_number = camera_data.sample; next_state.sampling_number = camera_data.sample;
next_state.timestamp = next_state.timestamp + 131; next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->clustering_lifo.WriteNextEntry(next_state); shared_memory->clustering_lifo.WriteNextEntry(next_state);

View file

@ -8,6 +8,10 @@
#include "core/hle/service/hid/irs_ring_lifo.h" #include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core {
class System;
}
namespace Core::HID { namespace Core::HID {
class EmulatedController; class EmulatedController;
} // namespace Core::HID } // namespace Core::HID
@ -15,8 +19,7 @@ class EmulatedController;
namespace Service::IRS { namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase { class ClusteringProcessor final : public ProcessorBase {
public: public:
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index); std::size_t npad_index);
~ClusteringProcessor() override; ~ClusteringProcessor() override;
@ -106,5 +109,7 @@ private:
Core::IrSensor::DeviceFormat& device; Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device; Core::HID::EmulatedController* npad_device;
int callback_key{}; int callback_key{};
Core::System& system;
}; };
} // namespace Service::IRS } // namespace Service::IRS

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/moment_processor.h" #include "core/hle/service/hid/irsensor/moment_processor.h"
@ -10,11 +12,10 @@ static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Siz
static constexpr std::size_t ImageWidth = 40; static constexpr std::size_t ImageWidth = 40;
static constexpr std::size_t ImageHeight = 30; static constexpr std::size_t ImageHeight = 30;
MomentProcessor::MomentProcessor(Core::HID::HIDCore& hid_core_, MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index) std::size_t npad_index)
: device(device_format) { : device(device_format), system{system_} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
@ -69,7 +70,7 @@ void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type)
} }
next_state.sampling_number = camera_data.sample; next_state.sampling_number = camera_data.sample;
next_state.timestamp = next_state.timestamp + 131; next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->moment_lifo.WriteNextEntry(next_state); shared_memory->moment_lifo.WriteNextEntry(next_state);

View file

@ -9,11 +9,19 @@
#include "core/hle/service/hid/irs_ring_lifo.h" #include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS { namespace Service::IRS {
class MomentProcessor final : public ProcessorBase { class MomentProcessor final : public ProcessorBase {
public: public:
explicit MomentProcessor(Core::HID::HIDCore& hid_core_, explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
Core::IrSensor::DeviceFormat& device_format, std::size_t npad_index); std::size_t npad_index);
~MomentProcessor() override; ~MomentProcessor() override;
// Called when the processor is initialized // Called when the processor is initialized
@ -76,6 +84,8 @@ private:
Core::IrSensor::DeviceFormat& device; Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device; Core::HID::EmulatedController* npad_device;
int callback_key{}; int callback_key{};
Core::System& system;
}; };
} // namespace Service::IRS } // namespace Service::IRS

View file

@ -1,8 +1,10 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-FileCopyrightText: 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <mutex>
#include <span> #include <span>
#include "common/assert.h" #include "common/assert.h"
@ -10,6 +12,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/page_table.h" #include "common/page_table.h"
#include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h" #include "core/core.h"
@ -318,7 +321,7 @@ struct Memory::Impl {
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
u8* const host_ptr) { u8* const host_ptr) {
if constexpr (!UNSAFE) { if constexpr (!UNSAFE) {
system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
} }
std::memcpy(host_ptr, src_buffer, copy_amount); std::memcpy(host_ptr, src_buffer, copy_amount);
}, },
@ -351,7 +354,7 @@ struct Memory::Impl {
}, },
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
u8* const host_ptr) { u8* const host_ptr) {
system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
std::memset(host_ptr, 0, copy_amount); std::memset(host_ptr, 0, copy_amount);
}, },
[](const std::size_t copy_amount) {}); [](const std::size_t copy_amount) {});
@ -420,7 +423,7 @@ struct Memory::Impl {
const std::size_t block_size) { const std::size_t block_size) {
// dc cvac: Store to point of coherency // dc cvac: Store to point of coherency
// CPU flush -> GPU invalidate // CPU flush -> GPU invalidate
system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
}; };
return PerformCacheOperation(dest_addr, size, on_rasterizer); return PerformCacheOperation(dest_addr, size, on_rasterizer);
} }
@ -430,7 +433,7 @@ struct Memory::Impl {
const std::size_t block_size) { const std::size_t block_size) {
// dc civac: Store to point of coherency, and invalidate from cache // dc civac: Store to point of coherency, and invalidate from cache
// CPU flush -> GPU invalidate // CPU flush -> GPU invalidate
system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
}; };
return PerformCacheOperation(dest_addr, size, on_rasterizer); return PerformCacheOperation(dest_addr, size, on_rasterizer);
} }
@ -767,7 +770,18 @@ struct Memory::Impl {
} }
void HandleRasterizerWrite(VAddr address, size_t size) { void HandleRasterizerWrite(VAddr address, size_t size) {
const size_t core = system.GetCurrentHostThreadID(); constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
const size_t core = std::min(system.GetCurrentHostThreadID(),
sys_core); // any other calls threads go to syscore.
// Guard on sys_core;
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
}
SCOPE_EXIT({
if (core == sys_core) [[unlikely]] {
sys_core_guard.unlock();
}
});
auto& current_area = rasterizer_write_areas[core]; auto& current_area = rasterizer_write_areas[core];
VAddr subaddress = address >> YUZU_PAGEBITS; VAddr subaddress = address >> YUZU_PAGEBITS;
bool do_collection = current_area.last_address == subaddress; bool do_collection = current_area.last_address == subaddress;
@ -799,6 +813,7 @@ struct Memory::Impl {
rasterizer_read_areas{}; rasterizer_read_areas{};
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
std::mutex sys_core_guard;
}; };
Memory::Memory(Core::System& system_) : system{system_} { Memory::Memory(Core::System& system_) : system{system_} {

View file

@ -86,10 +86,7 @@ public:
uncommitted_operations.emplace_back(std::move(func)); uncommitted_operations.emplace_back(std::move(func));
} }
pending_operations.emplace_back(std::move(uncommitted_operations)); pending_operations.emplace_back(std::move(uncommitted_operations));
{
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
QueueFence(new_fence); QueueFence(new_fence);
}
if (!delay_fence) { if (!delay_fence) {
func(); func();
} }

View file

@ -132,16 +132,12 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
const bool use_accelerated = const bool use_accelerated =
rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
const bool is_srgb = use_accelerated && screen_info.is_srgb; const bool is_srgb = use_accelerated && screen_info.is_srgb;
{
std::scoped_lock lock{rasterizer.LockCaches()};
RenderScreenshot(*framebuffer, use_accelerated); RenderScreenshot(*framebuffer, use_accelerated);
Frame* frame = present_manager.GetRenderFrame(); Frame* frame = present_manager.GetRenderFrame();
blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
scheduler.Flush(*frame->render_ready); scheduler.Flush(*frame->render_ready);
present_manager.Present(frame); present_manager.Present(frame);
}
gpu.RendererFrameEndNotify(); gpu.RendererFrameEndNotify();
rasterizer.TickFrame(); rasterizer.TickFrame();

View file

@ -199,7 +199,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
if (!pipeline) { if (!pipeline) {
return; return;
} }
std::scoped_lock lock{LockCaches()}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
// update engine as channel may be different. // update engine as channel may be different.
pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed); pipeline->Configure(is_indexed);
@ -710,7 +710,6 @@ void RasterizerVulkan::TiledCacheBarrier() {
} }
void RasterizerVulkan::FlushCommands() { void RasterizerVulkan::FlushCommands() {
std::scoped_lock lock{LockCaches()};
if (draw_counter == 0) { if (draw_counter == 0) {
return; return;
} }
@ -808,7 +807,6 @@ void RasterizerVulkan::FlushWork() {
if ((++draw_counter & 7) != 7) { if ((++draw_counter & 7) != 7) {
return; return;
} }
std::scoped_lock lock{LockCaches()};
if (draw_counter < DRAWS_TO_DISPATCH) { if (draw_counter < DRAWS_TO_DISPATCH) {
// Send recorded tasks to the worker thread // Send recorded tasks to the worker thread
scheduler.DispatchWork(); scheduler.DispatchWork();
@ -1507,7 +1505,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
CreateChannel(channel); CreateChannel(channel);
{ {
std::scoped_lock lock{LockCaches()}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
texture_cache.CreateChannel(channel); texture_cache.CreateChannel(channel);
buffer_cache.CreateChannel(channel); buffer_cache.CreateChannel(channel);
} }
@ -1520,7 +1518,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
const s32 channel_id = channel.bind_id; const s32 channel_id = channel.bind_id;
BindToChannel(channel_id); BindToChannel(channel_id);
{ {
std::scoped_lock lock{LockCaches()}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
texture_cache.BindToChannel(channel_id); texture_cache.BindToChannel(channel_id);
buffer_cache.BindToChannel(channel_id); buffer_cache.BindToChannel(channel_id);
} }
@ -1533,7 +1531,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
void RasterizerVulkan::ReleaseChannel(s32 channel_id) { void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
EraseChannel(channel_id); EraseChannel(channel_id);
{ {
std::scoped_lock lock{LockCaches()}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
texture_cache.EraseChannel(channel_id); texture_cache.EraseChannel(channel_id);
buffer_cache.EraseChannel(channel_id); buffer_cache.EraseChannel(channel_id);
} }

View file

@ -133,10 +133,6 @@ public:
void ReleaseChannel(s32 channel_id) override; void ReleaseChannel(s32 channel_id) override;
std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() {
return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex};
}
private: private:
static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_TEXTURES = 192;
static constexpr size_t MAX_IMAGES = 48; static constexpr size_t MAX_IMAGES = 48;

View file

@ -360,6 +360,7 @@ void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio")); qt_config->beginGroup(QStringLiteral("Audio"));
ReadCategory(Settings::Category::Audio); ReadCategory(Settings::Category::Audio);
ReadCategory(Settings::Category::UiAudio);
qt_config->endGroup(); qt_config->endGroup();
} }
@ -900,6 +901,7 @@ void Config::SaveAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio")); qt_config->beginGroup(QStringLiteral("Audio"));
WriteCategory(Settings::Category::Audio); WriteCategory(Settings::Category::Audio);
WriteCategory(Settings::Category::UiAudio);
qt_config->endGroup(); qt_config->endGroup();
} }

View file

@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
std::map<u32, QWidget*> hold; std::map<u32, QWidget*> hold;
auto push = [&](Settings::Category category) { auto push_settings = [&](Settings::Category category) {
for (auto* setting : Settings::values.linkage.by_category[category]) { for (auto* setting : Settings::values.linkage.by_category[category]) {
settings.push_back(setting); settings.push_back(setting);
} }
};
auto push_ui_settings = [&](Settings::Category category) {
for (auto* setting : UISettings::values.linkage.by_category[category]) { for (auto* setting : UISettings::values.linkage.by_category[category]) {
settings.push_back(setting); settings.push_back(setting);
} }
}; };
push(Settings::Category::Audio); push_settings(Settings::Category::Audio);
push(Settings::Category::SystemAudio); push_settings(Settings::Category::SystemAudio);
push_ui_settings(Settings::Category::UiAudio);
for (auto* setting : settings) { for (auto* setting : settings) {
auto* widget = builder.BuildWidget(setting, apply_funcs); auto* widget = builder.BuildWidget(setting, apply_funcs);

View file

@ -1065,12 +1065,6 @@ void GMainWindow::InitializeWidgets() {
volume_slider->setObjectName(QStringLiteral("volume_slider")); volume_slider->setObjectName(QStringLiteral("volume_slider"));
volume_slider->setMaximum(200); volume_slider->setMaximum(200);
volume_slider->setPageStep(5); volume_slider->setPageStep(5);
connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
Settings::values.audio_muted = false;
const auto volume = static_cast<u8>(percentage);
Settings::values.volume.SetValue(volume);
UpdateVolumeUI();
});
volume_popup->layout()->addWidget(volume_slider); volume_popup->layout()->addWidget(volume_slider);
volume_button = new VolumeButton(); volume_button = new VolumeButton();
@ -1078,6 +1072,12 @@ void GMainWindow::InitializeWidgets() {
volume_button->setFocusPolicy(Qt::NoFocus); volume_button->setFocusPolicy(Qt::NoFocus);
volume_button->setCheckable(true); volume_button->setCheckable(true);
UpdateVolumeUI(); UpdateVolumeUI();
connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
Settings::values.audio_muted = false;
const auto volume = static_cast<u8>(percentage);
Settings::values.volume.SetValue(volume);
UpdateVolumeUI();
});
connect(volume_button, &QPushButton::clicked, this, [&] { connect(volume_button, &QPushButton::clicked, this, [&] {
UpdateVolumeUI(); UpdateVolumeUI();
volume_popup->setVisible(!volume_popup->isVisible()); volume_popup->setVisible(!volume_popup->isVisible());

View file

@ -109,9 +109,13 @@ struct Values {
Settings::Specialization::Default, Settings::Specialization::Default,
true, true,
true}; true};
Setting<bool> mute_when_in_background{ Setting<bool> mute_when_in_background{linkage,
linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, false,
true, true}; "muteWhenInBackground",
Category::UiAudio,
Settings::Specialization::Default,
true,
true};
Setting<bool> hide_mouse{ Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true}; true, true};