Compare commits

..

5 Commits

Author SHA1 Message Date
yuzubot
252e8acde5 Android 217 2024-01-28 20:07:43 +00:00
yuzubot
c867143204 Merge yuzu-emu#12830 2024-01-28 20:07:42 +00:00
yuzubot
d0f90ff4ed Merge yuzu-emu#12814 2024-01-28 20:07:42 +00:00
yuzubot
d62f40109f Merge yuzu-emu#12760 2024-01-28 20:07:42 +00:00
yuzubot
508c950c87 Merge yuzu-emu#12749 2024-01-28 20:07:42 +00:00
116 changed files with 2119 additions and 3762 deletions

View File

@@ -73,7 +73,7 @@ jobs:
build-mac:
name: 'test build (macos)'
needs: format
runs-on: macos-14
runs-on: macos-13
steps:
- uses: actions/checkout@v3
with:
@@ -87,7 +87,7 @@ jobs:
run: |
mkdir build
cd build
export Qt5_DIR="$(brew --prefix qt@5)/lib/cmake"
export Qt5_DIR="/usr/local/opt/qt@5/lib/cmake"
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
ninja
build-msvc:

3
.gitmodules vendored
View File

@@ -64,6 +64,3 @@
[submodule "oaknut"]
path = externals/oaknut
url = https://github.com/merryhime/oaknut
[submodule "Vulkan-Utility-Libraries"]
path = externals/Vulkan-Utility-Libraries
url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git

View File

@@ -36,8 +36,6 @@ option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON)
option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan-Utility-Libraries from externals" ON)
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@@ -310,10 +308,6 @@ if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
find_package(Vulkan 1.3.274 REQUIRED)
endif()
if (NOT YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
find_package(VulkanUtilityLibraries REQUIRED)
endif()
if (ENABLE_LIBUSB)
find_package(libusb 1.0.24 MODULE)
endif()
@@ -322,10 +316,6 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
find_package(xbyak 6 CONFIG)
endif()
if (ARCHITECTURE_arm64)
find_package(oaknut 2.0.1 CONFIG)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
find_package(dynarmic 6.4.0 CONFIG)
endif()

View File

@@ -1,10 +1,9 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12461](https://github.com/yuzu-emu/yuzu-android//pull/12461) | [`e1f31cfe5`](https://github.com/yuzu-emu/yuzu-android//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12848](https://github.com/yuzu-emu/yuzu-android//pull/12848) | [`4afca6bf5`](https://github.com/yuzu-emu/yuzu-android//pull/12848/files) | service: capsrv: Migrate to new IPC | [german77](https://github.com/german77/) | Yes |
| [12874](https://github.com/yuzu-emu/yuzu-android//pull/12874) | [`f410cf681`](https://github.com/yuzu-emu/yuzu-android//pull/12874/files) | Revert "shader_recompiler: fix Offset operand usage for non-OpImage*Gather" | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12875](https://github.com/yuzu-emu/yuzu-android//pull/12875) | [`5cb9fe781`](https://github.com/yuzu-emu/yuzu-android//pull/12875/files) | SwBlitter: Fix Pitch linear reading/writting | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12760](https://github.com/yuzu-emu/yuzu-android//pull/12760) | [`c6b88b62d`](https://github.com/yuzu-emu/yuzu-android//pull/12760/files) | am: rewrite for multiprocess support | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12814](https://github.com/yuzu-emu/yuzu-android//pull/12814) | [`da410506a`](https://github.com/yuzu-emu/yuzu-android//pull/12814/files) | Move time services to new IPC and add debug printing | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12830](https://github.com/yuzu-emu/yuzu-android//pull/12830) | [`6cc82fd43`](https://github.com/yuzu-emu/yuzu-android//pull/12830/files) | externals/dynarmic: Update to 6.6.1 | [merryhime](https://github.com/merryhime/) | Yes |
End of merge log. You can find the original README.md below the break.

View File

@@ -14,17 +14,16 @@ set(BUILD_SHARED_LIBS OFF)
# Skip install rules for all externals
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# Xbyak (also used by Dynarmic, so needs to be added first)
# xbyak
if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
add_subdirectory(xbyak)
endif()
# Oaknut (also used by Dynarmic, so needs to be added first)
# Dynarmic
if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut)
add_subdirectory(oaknut)
endif()
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic)
@@ -155,11 +154,6 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
add_subdirectory(Vulkan-Headers)
endif()
# Vulkan-Utility-Libraries
if (YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
add_subdirectory(Vulkan-Utility-Libraries)
endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)

View File

@@ -261,7 +261,7 @@ object NativeLibrary {
/**
* Begins emulation.
*/
external fun run(path: String?, programIndex: Int, frontendInitiated: Boolean)
external fun run(path: String?, programIndex: Int = 0)
// Surface Handling
external fun surfaceChanged(surf: Surface?)

View File

@@ -7,7 +7,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
@@ -58,9 +57,13 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
title.text = model.title
version.text = model.version
description.text = model.description
if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) {
if (model.description.isNotEmpty()) {
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
} else {
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
}
}

View File

@@ -927,7 +927,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationThread.join()
emulationThread = Thread({
Log.debug("[EmulationFragment] Starting emulation thread.")
NativeLibrary.run(gamePath, programIndex, false)
NativeLibrary.run(gamePath, programIndex)
}, "NativeEmulation")
emulationThread.start()
}
@@ -981,7 +981,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
State.STOPPED -> {
emulationThread = Thread({
Log.debug("[EmulationFragment] Starting emulation thread.")
NativeLibrary.run(gamePath, programIndex, true)
NativeLibrary.run(gamePath, programIndex)
}, "NativeEmulation")
emulationThread.start()
}

View File

@@ -219,8 +219,7 @@ void EmulationSession::SetAppletId(int applet_id) {
}
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
const std::size_t program_index,
const bool frontend_initiated) {
const std::size_t program_index) {
std::scoped_lock lock(m_mutex);
// Create the render window.
@@ -252,8 +251,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
// Load the ROM.
Service::AM::FrontendAppletParameters params{
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
: Service::AM::LaunchType::ApplicationInitiated,
.program_index = static_cast<s32>(program_index),
};
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
@@ -450,8 +447,7 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
}
static Core::SystemResultStatus RunEmulation(const std::string& filepath,
const size_t program_index,
const bool frontend_initiated) {
const size_t program_index = 0) {
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -464,8 +460,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
frontend_initiated);
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index);
if (result != Core::SystemResultStatus::Success) {
return result;
}
@@ -762,12 +757,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
jint j_program_index,
jboolean j_frontend_initiated) {
jint j_program_index) {
const std::string path = GetJString(env, j_path);
const Core::SystemResultStatus result{
RunEmulation(path, j_program_index, j_frontend_initiated)};
const Core::SystemResultStatus result{RunEmulation(path, j_program_index)};
if (result != Core::SystemResultStatus::Success) {
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetExitEmulationActivity(), static_cast<int>(result));

View File

@@ -47,8 +47,7 @@ public:
void InitializeSystem(bool reload);
void SetAppletId(int applet_id);
Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
const std::size_t program_index,
const bool frontend_initiated);
const std::size_t program_index = 0);
bool IsHandheldOnly();
void SetDeviceType([[maybe_unused]] int index, int type);

View File

@@ -134,12 +134,12 @@ struct Values {
Linkage linkage{};
// Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_output_device_id{
linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_input_device_id{
linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList};
Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio,
Specialization::RuntimeList};
Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio,
Specialization::RuntimeList};
Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio,
Specialization::RuntimeList};
SwitchableSetting<AudioMode, true> sound_index{
linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
"sound_index", Category::SystemAudio, Specialization::Default, true,

View File

@@ -5,7 +5,6 @@
#include <array>
#include <atomic>
#include <bit>
#include <deque>
#include <memory>
#include <mutex>
@@ -43,8 +42,6 @@ public:
DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager();
static constexpr bool HAS_FLUSH_INVALIDATION = true;
void BindInterface(DeviceInterface* device_inter);
DAddr Allocate(size_t size);
@@ -184,28 +181,24 @@ private:
}
Common::VirtualBuffer<VAddr> cpu_backing_address;
using CounterType = u8;
using CounterAtomicType = std::atomic_uint8_t;
static constexpr size_t subentries = 8 / sizeof(CounterType);
static constexpr size_t subentries = 8 / sizeof(u8);
static constexpr size_t subentries_mask = subentries - 1;
static constexpr size_t subentries_shift =
std::countr_zero(sizeof(u64)) - std::countr_zero(sizeof(CounterType));
class CounterEntry final {
public:
CounterEntry() = default;
CounterAtomicType& Count(std::size_t page) {
std::atomic_uint8_t& Count(std::size_t page) {
return values[page & subentries_mask];
}
const CounterAtomicType& Count(std::size_t page) const {
const std::atomic_uint8_t& Count(std::size_t page) const {
return values[page & subentries_mask];
}
private:
std::array<CounterAtomicType, subentries> values{};
std::array<std::atomic_uint8_t, subentries> values{};
};
static_assert(sizeof(CounterEntry) == subentries * sizeof(CounterType),
static_assert(sizeof(CounterEntry) == subentries * sizeof(u8),
"CounterEntry should be 8 bytes!");
static constexpr size_t num_counter_entries =

View File

@@ -213,8 +213,8 @@ void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, Asid asid,
bool track) {
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size,
Asid asid, bool track) {
Core::Memory::Memory* process_memory = registered_processes[asid.id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
@@ -519,36 +519,22 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
size_t page = addr >> Memory::YUZU_PAGEBITS;
auto [asid, base_vaddress] = ExtractCPUBacking(page);
size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
auto* memory_device_inter = registered_processes[asid.id];
const auto release_pending = [&] {
if (uncache_bytes > 0) {
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false);
uncache_bytes = 0;
}
if (cache_bytes > 0) {
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
cache_bytes, true);
cache_bytes = 0;
}
};
for (; page != page_end; ++page) {
CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
auto [asid_2, vpage] = ExtractCPUBacking(page);
vpage >>= Memory::YUZU_PAGEBITS;
std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page);
if (vpage == 0) [[unlikely]] {
release_pending();
continue;
}
if (asid.id != asid_2.id) [[unlikely]] {
release_pending();
memory_device_inter = registered_processes[asid_2.id];
if (delta > 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(),
"Count may overflow!");
} else if (delta < 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!");
} else {
ASSERT_MSG(false, "Delta must be non-zero!");
}
// Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
count.fetch_add(static_cast<u8>(delta), std::memory_order_release);
// Assume delta is either -1 or 1
if (count.load(std::memory_order::relaxed) == 0) {
@@ -567,12 +553,20 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
}
cache_bytes += Memory::YUZU_PAGESIZE;
} else if (cache_bytes > 0) {
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
cache_bytes, true);
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
cache_bytes = 0;
}
vpage++;
}
if (uncache_bytes > 0) {
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
false);
}
if (cache_bytes > 0) {
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
}
release_pending();
}
} // namespace Core

View File

@@ -44,32 +44,15 @@ public:
GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{&memory}, m_addr{addr}, m_size{size} {
: m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
m_span_valid = true;
m_is_data_copy = true;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
m_span_valid = true;
m_is_data_copy = true;
}
}
} else if constexpr (FLAGS & GuestMemoryFlags::Read) {
if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup);
}
}
~GuestMemory() = default;
GuestMemory(GuestMemory&& rhs) = default;
GuestMemory& operator=(GuestMemory&& rhs) = default;
T* data() noexcept {
return m_data_span.data();
}
@@ -126,8 +109,8 @@ public:
}
if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
m_memory->FlushRegion(m_addr, this->size_bytes());
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.FlushRegion(m_addr, this->size_bytes());
}
} else {
if (backup) {
@@ -140,9 +123,9 @@ public:
m_is_data_copy = true;
m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory->ReadBlock(m_addr, this->data(), this->size_bytes());
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else {
m_memory->ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
}
}
return m_data_span;
@@ -150,19 +133,18 @@ public:
void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory->WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory->WriteBlock(m_addr, write_data.data(), this->size_bytes());
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else {
m_memory->WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
}
}
bool TrySetSpan() noexcept {
if (u8* ptr = m_memory->GetSpan(m_addr, this->size_bytes()); ptr) {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true;
m_is_data_copy = false;
return true;
}
return false;
@@ -177,7 +159,7 @@ protected:
return m_addr_changed;
}
M* m_memory;
M& m_memory;
u64 m_addr{};
size_t m_size{};
std::span<T> m_data_span{};
@@ -193,7 +175,17 @@ public:
GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {}
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) {
@@ -204,17 +196,15 @@ public:
if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory->WriteBlockCached(this->m_addr, this->data(),
this->size_bytes());
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory->WriteBlock(this->m_addr, this->data(), this->size_bytes());
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else {
this->m_memory->WriteBlockUnsafe(this->m_addr, this->data(),
this->size_bytes());
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory->InvalidateRegion(this->m_addr, this->size_bytes());
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
}

View File

@@ -303,8 +303,8 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
}
// Applet was started by frontend, so it is foreground.
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus;
this->InsertApplet(std::move(applet));

View File

@@ -207,7 +207,7 @@ void Error::Execute() {
void Error::DisplayCompleted() {
complete = true;
PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
PushOutData(std::make_shared<IStorage>(system, std::vector<u8>()));
Exit();
}

View File

@@ -85,26 +85,24 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
applet->library_applet_mode = mode;
// Set focus state
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
switch (mode) {
case LibraryAppletMode::AllForeground:
case LibraryAppletMode::NoUI:
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus;
applet->hid_registration.EnableAppletToGetInput(true);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break;
case LibraryAppletMode::AllForegroundInitiallyHidden:
applet->system_buffer_manager.SetWindowVisibility(false);
applet->focus_state = FocusState::NotInFocus;
applet->hid_registration.EnableAppletToGetInput(false);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break;
case LibraryAppletMode::Background:
case LibraryAppletMode::BackgroundIndirectDisplay:
default:
applet->focus_state = FocusState::Background;
applet->hid_registration.EnableAppletToGetInput(true);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break;
}

View File

@@ -202,7 +202,7 @@ void AOC_U::ListAddOnContent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
process_id);
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
const auto current = system.GetApplicationProcessProgramID();
std::vector<u32> out;
const auto& disabled = Settings::values.disabled_addons[current];

View File

@@ -5,7 +5,7 @@
#include "core/hle/service/caps/caps_a.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
@@ -18,9 +18,9 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
{0, nullptr, "GetAlbumFileCount"},
{1, nullptr, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"},
{3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
{3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"},
{5, C<&IAlbumAccessorService::IsAlbumMounted>, "IsAlbumMounted"},
{5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"},
{6, nullptr, "GetAlbumUsage"},
{7, nullptr, "GetAlbumFileSize"},
{8, nullptr, "LoadAlbumFileThumbnail"},
@@ -33,18 +33,18 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
{15, nullptr, "GetAlbumUsage3"},
{16, nullptr, "GetAlbumMountResult"},
{17, nullptr, "GetAlbumUsage16"},
{18, C<&IAlbumAccessorService::Unknown18>, "Unknown18"},
{18, &IAlbumAccessorService::Unknown18, "Unknown18"},
{19, nullptr, "Unknown19"},
{100, nullptr, "GetAlbumFileCountEx0"},
{101, C<&IAlbumAccessorService::GetAlbumFileListEx0>, "GetAlbumFileListEx0"},
{101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"},
{202, nullptr, "SaveEditedScreenShot"},
{301, nullptr, "GetLastThumbnail"},
{302, nullptr, "GetLastOverlayMovieThumbnail"},
{401, C<&IAlbumAccessorService::GetAutoSavingStorage>, "GetAutoSavingStorage"},
{401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"},
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
{1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
{1002, C<&IAlbumAccessorService::LoadAlbumScreenShotImageEx1>, "LoadAlbumScreenShotImageEx1"},
{1003, C<&IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1>, "LoadAlbumScreenShotThumbnailImageEx1"},
{1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"},
{1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"},
{8001, nullptr, "ForceAlbumUnmounted"},
{8002, nullptr, "ResetAlbumMountStatus"},
{8011, nullptr, "RefreshAlbumCache"},
@@ -62,70 +62,138 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
IAlbumAccessorService::~IAlbumAccessorService() = default;
Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_id{rp.PopRaw<AlbumFileId>()};
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
file_id.application_id, file_id.storage, file_id.type);
const Result result = manager->DeleteAlbumFile(file_id);
R_RETURN(TranslateResult(result));
Result result = manager->DeleteAlbumFile(file_id);
result = TranslateResult(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
Result IAlbumAccessorService::IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage) {
void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage{rp.PopEnum<AlbumStorage>()};
LOG_INFO(Service_Capture, "called, storage={}", storage);
const Result result = manager->IsAlbumMounted(storage);
*out_is_mounted = result.IsSuccess();
R_RETURN(TranslateResult(result));
Result result = manager->IsAlbumMounted(storage);
const bool is_mounted = result.IsSuccess();
result = TranslateResult(result);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push<u8>(is_mounted);
}
Result IAlbumAccessorService::Unknown18(
Out<u32> out_buffer_size,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer) {
void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) {
struct UnknownBuffer {
INSERT_PADDING_BYTES(0x10);
};
static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size");
LOG_WARNING(Service_Capture, "(STUBBED) called");
*out_buffer_size = 0;
R_SUCCEED();
std::vector<UnknownBuffer> buffer{};
if (!buffer.empty()) {
ctx.WriteBuffer(buffer);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(buffer.size()));
}
Result IAlbumAccessorService::GetAlbumFileListEx0(
Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage{rp.PopEnum<AlbumStorage>()};
const auto flags{rp.Pop<u8>()};
const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()};
LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);
const Result result = manager->GetAlbumFileList(out_entries, *out_entries_size, storage, flags);
R_RETURN(TranslateResult(result));
std::vector<AlbumEntry> entries;
Result result = manager->GetAlbumFileList(entries, storage, flags);
result = TranslateResult(result);
entries.resize(std::min(album_entry_size, entries.size()));
if (!entries.empty()) {
ctx.WriteBuffer(entries);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push<u64>(entries.size());
}
Result IAlbumAccessorService::GetAutoSavingStorage(Out<bool> out_is_autosaving) {
void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) {
LOG_WARNING(Service_Capture, "(STUBBED) called");
const Result result = manager->GetAutoSavingStorage(*out_is_autosaving);
R_RETURN(TranslateResult(result));
bool is_autosaving{};
Result result = manager->GetAutoSavingStorage(is_autosaving);
result = TranslateResult(result);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push<u8>(is_autosaving);
}
Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1(
const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_id{rp.PopRaw<AlbumFileId>()};
const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()};
const auto image_buffer_size{ctx.GetWriteBufferSize(1)};
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
const Result result =
manager->LoadAlbumScreenShotImage(*out_image_output, out_image, file_id, decoder_options);
R_RETURN(TranslateResult(result));
std::vector<u8> image;
LoadAlbumScreenShotImageOutput image_output;
Result result =
manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options);
result = TranslateResult(result);
if (image.size() > image_buffer_size) {
result = ResultWorkMemoryError;
}
if (result.IsSuccess()) {
ctx.WriteBuffer(image_output, 0);
ctx.WriteBuffer(image, 1);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(
const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_id{rp.PopRaw<AlbumFileId>()};
const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()};
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image,
file_id, decoder_options);
R_RETURN(TranslateResult(result));
std::vector<u8> image(ctx.GetWriteBufferSize(1));
LoadAlbumScreenShotImageOutput image_output;
Result result =
manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options);
result = TranslateResult(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(image_output, 0);
ctx.WriteBuffer(image, 1);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
Result IAlbumAccessorService::TranslateResult(Result in_result) {

View File

@@ -3,8 +3,6 @@
#pragma once
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -21,31 +19,13 @@ public:
~IAlbumAccessorService() override;
private:
Result DeleteAlbumFile(AlbumFileId file_id);
Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);
Result Unknown18(
Out<u32> out_buffer_size,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
out_buffer);
Result GetAlbumFileListEx0(Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);
Result GetAutoSavingStorage(Out<bool> out_is_autosaving);
Result LoadAlbumScreenShotImageEx1(
const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);
Result LoadAlbumScreenShotThumbnailImageEx1(
const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);
void DeleteAlbumFile(HLERequestContext& ctx);
void IsAlbumMounted(HLERequestContext& ctx);
void Unknown18(HLERequestContext& ctx);
void GetAlbumFileListEx0(HLERequestContext& ctx);
void GetAutoSavingStorage(HLERequestContext& ctx);
void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx);
void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx);
Result TranslateResult(Result in_result);

View File

@@ -6,7 +6,6 @@
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
@@ -18,7 +17,7 @@ IAlbumControlService::IAlbumControlService(Core::System& system_,
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
{33, C<&IAlbumControlService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
{33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -43,11 +42,16 @@ IAlbumControlService::IAlbumControlService(Core::System& system_,
IAlbumControlService::~IAlbumControlService() = default;
Result IAlbumControlService::SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid) {
void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
library_version, aruid.pid);
R_SUCCEED();
library_version, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::Capture

View File

@@ -3,7 +3,6 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -12,7 +11,6 @@ class System;
namespace Service::Capture {
class AlbumManager;
enum class ShimLibraryVersion : u64;
class IAlbumControlService final : public ServiceFramework<IAlbumControlService> {
public:
@@ -21,8 +19,7 @@ public:
~IAlbumControlService() override;
private:
Result SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid);
void SetShimLibraryVersion(HLERequestContext& ctx);
std::shared_ptr<AlbumManager> manager = nullptr;
};

View File

@@ -58,8 +58,8 @@ Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
return is_mounted ? ResultSuccess : ResultIsNotMounted;
}
Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
AlbumStorage storage, u8 flags) const {
Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
u8 flags) const {
if (storage > AlbumStorage::Sd) {
return ResultInvalidStorage;
}
@@ -72,55 +72,51 @@ Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& ou
if (file_id.storage != storage) {
continue;
}
if (out_entries_count >= SdAlbumFileLimit) {
break;
}
if (out_entries_count >= out_entries.size()) {
if (out_entries.size() >= SdAlbumFileLimit) {
break;
}
const auto entry_size = Common::FS::GetSize(path);
out_entries[out_entries_count++] = {
out_entries.push_back({
.entry_size = entry_size,
.file_id = file_id,
};
});
}
return ResultSuccess;
}
Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
u64& out_entries_count, ContentType content_type,
s64 start_posix_time, s64 end_posix_time, u64 aruid) const {
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
ContentType content_type, s64 start_posix_time,
s64 end_posix_time, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
}
std::vector<ApplicationAlbumEntry> album_entries(out_entries.size());
std::vector<ApplicationAlbumEntry> album_entries;
const auto start_date = ConvertToAlbumDateTime(start_posix_time);
const auto end_date = ConvertToAlbumDateTime(end_posix_time);
const auto result = GetAlbumFileList(album_entries, out_entries_count, content_type, start_date,
end_date, aruid);
const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid);
if (result.IsError()) {
return result;
}
for (std::size_t i = 0; i < out_entries_count; i++) {
out_entries[i] = {
.entry = album_entries[i],
.datetime = album_entries[i].datetime,
for (const auto& album_entry : album_entries) {
ApplicationAlbumFileEntry entry{
.entry = album_entry,
.datetime = album_entry.datetime,
.unknown = {},
};
out_entries.push_back(entry);
}
return ResultSuccess;
}
Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries,
u64& out_entries_count, ContentType content_type,
AlbumFileDateTime start_date, AlbumFileDateTime end_date,
u64 aruid) const {
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
ContentType content_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
}
@@ -135,15 +131,12 @@ Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entri
if (file_id.date < end_date) {
continue;
}
if (out_entries_count >= SdAlbumFileLimit) {
break;
}
if (out_entries_count >= out_entries.size()) {
if (out_entries.size() >= SdAlbumFileLimit) {
break;
}
const auto entry_size = Common::FS::GetSize(path);
out_entries[out_entries_count++] = {
ApplicationAlbumEntry entry{
.size = entry_size,
.hash{},
.datetime = file_id.date,
@@ -151,6 +144,7 @@ Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entri
.content = content_type,
.unknown = 1,
};
out_entries.push_back(entry);
}
return ResultSuccess;
@@ -162,7 +156,8 @@ Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
}
Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
std::span<u8> out_image, const AlbumFileId& file_id,
std::vector<u8>& out_image,
const AlbumFileId& file_id,
const ScreenShotDecodeOption& decoder_options) const {
if (file_id.storage > AlbumStorage::Sd) {
return ResultInvalidStorage;
@@ -181,9 +176,7 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou
.orientation = AlbumImageOrientation::None,
.unknown_1{},
.unknown_2{},
.pad163{},
},
.pad179{},
};
std::filesystem::path path;
@@ -193,12 +186,14 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou
return result;
}
out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
+static_cast<int>(out_image_output.height), decoder_options.flags);
}
Result AlbumManager::LoadAlbumScreenShotThumbnail(
LoadAlbumScreenShotImageOutput& out_image_output, std::span<u8> out_image,
LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image,
const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
if (file_id.storage > AlbumStorage::Sd) {
return ResultInvalidStorage;
@@ -217,9 +212,7 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail(
.orientation = AlbumImageOrientation::None,
.unknown_1{},
.unknown_2{},
.pad163{},
},
.pad179{},
};
std::filesystem::path path;
@@ -229,6 +222,8 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail(
return result;
}
out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
+static_cast<int>(out_image_output.height), decoder_options.flags);
}

View File

@@ -42,20 +42,20 @@ public:
Result DeleteAlbumFile(const AlbumFileId& file_id);
Result IsAlbumMounted(AlbumStorage storage);
Result GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
AlbumStorage storage, u8 flags) const;
Result GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
u64& out_entries_count, ContentType content_type, s64 start_posix_time,
s64 end_posix_time, u64 aruid) const;
Result GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, u64& out_entries_count,
Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
u8 flags) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
ContentType content_type, s64 start_posix_time, s64 end_posix_time,
u64 aruid) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
ContentType content_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const;
Result GetAutoSavingStorage(bool& out_is_autosaving) const;
Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
std::span<u8> out_image, const AlbumFileId& file_id,
std::vector<u8>& out_image, const AlbumFileId& file_id,
const ScreenShotDecodeOption& decoder_options) const;
Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output,
std::span<u8> out_image, const AlbumFileId& file_id,
std::vector<u8>& out_image, const AlbumFileId& file_id,
const ScreenShotDecodeOption& decoder_options) const;
Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute,

View File

@@ -3,10 +3,11 @@
#include "common/logging/log.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_ss.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/caps/caps_ss.h"
namespace Service::Capture {
IScreenShotService::IScreenShotService(Core::System& system_,
@@ -16,9 +17,9 @@ IScreenShotService::IScreenShotService(Core::System& system_,
static const FunctionInfo functions[] = {
{201, nullptr, "SaveScreenShot"},
{202, nullptr, "SaveEditedScreenShot"},
{203, C<&IScreenShotService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
{203, &IScreenShotService::SaveScreenShotEx0, "SaveScreenShotEx0"},
{204, nullptr, "SaveEditedScreenShotEx0"},
{206, C<&IScreenShotService::SaveEditedScreenShotEx1>, "SaveEditedScreenShotEx1"},
{206, &IScreenShotService::SaveEditedScreenShotEx1, "SaveEditedScreenShotEx1"},
{208, nullptr, "SaveScreenShotOfMovieEx1"},
{1000, nullptr, "Unknown1000"},
};
@@ -29,38 +30,69 @@ IScreenShotService::IScreenShotService(Core::System& system_,
IScreenShotService::~IScreenShotService() = default;
Result IScreenShotService::SaveScreenShotEx0(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer) {
void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ScreenShotAttribute attribute{};
AlbumReportOption report_option{};
INSERT_PADDING_BYTES(0x4);
u64 applet_resource_user_id{};
};
static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto image_data_buffer = ctx.ReadBuffer();
LOG_INFO(Service_Capture,
"called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
report_option, image_data_buffer.size(), aruid.pid);
parameters.report_option, image_data_buffer.size(),
parameters.applet_resource_user_id);
ApplicationAlbumEntry entry{};
manager->FlipVerticallyOnWrite(false);
R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
aruid.pid));
const auto result =
manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option,
image_data_buffer, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(result);
rb.PushRaw(entry);
}
Result IScreenShotService::SaveEditedScreenShotEx1(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
thumbnail_image_data_buffer) {
void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ScreenShotAttribute attribute;
u64 width;
u64 height;
u64 thumbnail_width;
u64 thumbnail_height;
AlbumFileId file_id;
};
static_assert(sizeof(Parameters) == 0x78, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto application_data_buffer = ctx.ReadBuffer(0);
const auto image_data_buffer = ctx.ReadBuffer(1);
const auto thumbnail_image_data_buffer = ctx.ReadBuffer(2);
LOG_INFO(Service_Capture,
"called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, "
"application_id={:016x}, storage={}, type={}, "
"application_id={:016x}, storage={}, type={}, app_data_buffer_size={}, "
"image_data_buffer_size={}, thumbnail_image_buffer_size={}",
width, height, thumbnail_width, thumbnail_height, file_id.application_id,
file_id.storage, file_id.type, image_data_buffer.size(),
thumbnail_image_data_buffer.size());
parameters.width, parameters.height, parameters.thumbnail_width,
parameters.thumbnail_height, parameters.file_id.application_id,
parameters.file_id.storage, parameters.file_id.type, application_data_buffer.size(),
image_data_buffer.size(), thumbnail_image_data_buffer.size());
ApplicationAlbumEntry entry{};
manager->FlipVerticallyOnWrite(false);
R_RETURN(manager->SaveEditedScreenShot(*out_entry, attribute, file_id, image_data_buffer));
const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute,
parameters.file_id, image_data_buffer);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(result);
rb.PushRaw(entry);
}
} // namespace Service::Capture

View File

@@ -3,8 +3,6 @@
#pragma once
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -19,20 +17,8 @@ public:
~IScreenShotService() override;
private:
Result SaveScreenShotEx0(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer);
Result SaveEditedScreenShotEx1(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
thumbnail_image_data_buffer);
void SaveScreenShotEx0(HLERequestContext& ctx);
void SaveEditedScreenShotEx1(HLERequestContext& ctx);
std::shared_ptr<AlbumManager> manager;
};

View File

@@ -6,7 +6,6 @@
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "video_core/renderer_base.h"
@@ -17,10 +16,10 @@ IScreenShotApplicationService::IScreenShotApplicationService(
: ServiceFramework{system_, "caps:su"}, manager{album_manager} {
// clang-format off
static const FunctionInfo functions[] = {
{32, C<&IScreenShotApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
{32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
{201, nullptr, "SaveScreenShot"},
{203, C<&IScreenShotApplicationService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
{205, C<&IScreenShotApplicationService::SaveScreenShotEx1>, "SaveScreenShotEx1"},
{203, &IScreenShotApplicationService::SaveScreenShotEx0, "SaveScreenShotEx0"},
{205, &IScreenShotApplicationService::SaveScreenShotEx1, "SaveScreenShotEx1"},
{210, nullptr, "SaveScreenShotEx2"},
};
// clang-format on
@@ -30,40 +29,77 @@ IScreenShotApplicationService::IScreenShotApplicationService(
IScreenShotApplicationService::~IScreenShotApplicationService() = default;
Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid) {
void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
library_version, aruid.pid);
R_SUCCEED();
library_version, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Result IScreenShotApplicationService::SaveScreenShotEx0(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer) {
void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ScreenShotAttribute attribute{};
AlbumReportOption report_option{};
INSERT_PADDING_BYTES(0x4);
u64 applet_resource_user_id{};
};
static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto image_data_buffer = ctx.ReadBuffer();
LOG_INFO(Service_Capture,
"called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
report_option, image_data_buffer.size(), aruid.pid);
parameters.report_option, image_data_buffer.size(),
parameters.applet_resource_user_id);
ApplicationAlbumEntry entry{};
manager->FlipVerticallyOnWrite(false);
R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
aruid.pid));
const auto result =
manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option,
image_data_buffer, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(result);
rb.PushRaw(entry);
}
Result IScreenShotApplicationService::SaveScreenShotEx1(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer) {
void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ScreenShotAttribute attribute{};
AlbumReportOption report_option{};
INSERT_PADDING_BYTES(0x4);
u64 applet_resource_user_id{};
};
static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto app_data_buffer = ctx.ReadBuffer(0);
const auto image_data_buffer = ctx.ReadBuffer(1);
LOG_INFO(Service_Capture,
"called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
report_option, image_data_buffer.size(), aruid.pid);
parameters.report_option, image_data_buffer.size(),
parameters.applet_resource_user_id);
ApplicationAlbumEntry entry{};
ApplicationData app_data{};
std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData));
manager->FlipVerticallyOnWrite(false);
R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, *app_data_buffer,
image_data_buffer, aruid.pid));
const auto result =
manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data,
image_data_buffer, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(result);
rb.PushRaw(entry);
}
void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) {
@@ -76,7 +112,6 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
.orientation = Capture::AlbumImageOrientation::None,
.unknown_1{},
.unknown_2{},
.pad163{},
};
renderer.RequestScreenshot(

View File

@@ -3,8 +3,6 @@
#pragma once
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -28,19 +26,9 @@ private:
static constexpr std::size_t screenshot_height = 720;
static constexpr std::size_t bytes_per_pixel = 4;
Result SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid);
Result SaveScreenShotEx0(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer);
Result SaveScreenShotEx1(
Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
AlbumReportOption report_option, ClientAppletResourceUserId aruid,
const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
image_data_buffer);
void SetShimLibraryVersion(HLERequestContext& ctx);
void SaveScreenShotEx0(HLERequestContext& ctx);
void SaveScreenShotEx1(HLERequestContext& ctx);
std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data;

View File

@@ -41,10 +41,6 @@ enum class ScreenShotDecoderFlag : u64 {
EnableBlockSmoothing = 1 << 1,
};
enum class ShimLibraryVersion : u64 {
Version1 = 1,
};
// This is nn::capsrv::AlbumFileDateTime
struct AlbumFileDateTime {
s16 year{};
@@ -148,23 +144,19 @@ static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
"ApplicationAlbumFileEntry has incorrect size.");
struct ApplicationData {
std::array<u8, 0x400> data;
u32 data_size;
std::array<u8, 0x400> data{};
u32 data_size{};
};
static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
static_assert(std::is_trivial_v<ApplicationData>,
"ApplicationData type must be trivially copyable.");
struct ScreenShotAttribute {
u32 unknown_0;
AlbumImageOrientation orientation;
u32 unknown_1;
u32 unknown_2;
INSERT_PADDING_BYTES_NOINIT(0x30);
u32 unknown_0{};
AlbumImageOrientation orientation{};
u32 unknown_1{};
u32 unknown_2{};
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
static_assert(std::is_trivial_v<ScreenShotAttribute>,
"ScreenShotAttribute type must be trivially copyable.");
struct ScreenShotDecodeOption {
ScreenShotDecoderFlag flags{};
@@ -173,15 +165,13 @@ struct ScreenShotDecodeOption {
static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size");
struct LoadAlbumScreenShotImageOutput {
s64 width;
s64 height;
ScreenShotAttribute attribute;
INSERT_PADDING_BYTES_NOINIT(0x400);
s64 width{};
s64 height{};
ScreenShotAttribute attribute{};
INSERT_PADDING_BYTES(0x400);
};
static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
"LoadAlbumScreenShotImageOutput is an invalid size");
static_assert(std::is_trivial_v<LoadAlbumScreenShotImageOutput>,
"LoadAlbumScreenShotImageOutput type must be trivially copyable.");
struct LoadAlbumScreenShotImageOutputForApplication {
s64 width{};

View File

@@ -5,7 +5,6 @@
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/caps/caps_u.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
@@ -15,8 +14,8 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
: ServiceFramework{system_, "caps:u"}, manager{album_manager} {
// clang-format off
static const FunctionInfo functions[] = {
{32, C<&IAlbumApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
{102, C<&IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated>, "GetAlbumFileList0AafeAruidDeprecated"},
{32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
{102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"},
{103, nullptr, "DeleteAlbumFileByAruid"},
{104, nullptr, "GetAlbumFileSizeByAruid"},
{105, nullptr, "DeleteAlbumFileByAruidForDebug"},
@@ -25,7 +24,7 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
{130, nullptr, "PrecheckToCreateContentsByAruid"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
{142, C<&IAlbumApplicationService::GetAlbumFileList3AaeAruid>, "GetAlbumFileList3AaeAruid"},
{142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{144, nullptr, "GetAllAlbumFileList3AaeAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
@@ -37,40 +36,101 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
IAlbumApplicationService::~IAlbumApplicationService() = default;
Result IAlbumApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid) {
void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
library_version, aruid.pid);
R_SUCCEED();
library_version, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Result IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(
Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, s64 end_posix_time,
ClientAppletResourceUserId aruid,
OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries) {
void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ContentType content_type;
INSERT_PADDING_BYTES(7);
s64 start_posix_time;
s64 end_posix_time;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_Capture,
"(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, "
"applet_resource_user_id={}",
content_type, start_posix_time, end_posix_time, aruid.pid);
parameters.content_type, parameters.start_posix_time, parameters.end_posix_time,
parameters.applet_resource_user_id);
R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
start_posix_time, end_posix_time, aruid.pid));
Result result = ResultSuccess;
if (result.IsSuccess()) {
result = manager->IsAlbumMounted(AlbumStorage::Sd);
}
std::vector<ApplicationAlbumFileEntry> entries;
if (result.IsSuccess()) {
result = manager->GetAlbumFileList(entries, parameters.content_type,
parameters.start_posix_time, parameters.end_posix_time,
parameters.applet_resource_user_id);
}
if (!entries.empty()) {
ctx.WriteBuffer(entries);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push<u64>(entries.size());
}
Result IAlbumApplicationService::GetAlbumFileList3AaeAruid(
Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
ContentType content_type;
INSERT_PADDING_BYTES(1);
AlbumFileDateTime start_date_time;
AlbumFileDateTime end_date_time;
INSERT_PADDING_BYTES(6);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_Capture,
"(STUBBED) called. content_type={}, start_date={}/{}/{}, "
"end_date={}/{}/{}, applet_resource_user_id={}",
content_type, start_date_time.year, start_date_time.month, start_date_time.day,
end_date_time.year, end_date_time.month, end_date_time.day, aruid.pid);
parameters.content_type, parameters.start_date_time.year,
parameters.start_date_time.month, parameters.start_date_time.day,
parameters.end_date_time.year, parameters.end_date_time.month,
parameters.end_date_time.day, parameters.applet_resource_user_id);
R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
start_date_time, end_date_time, aruid.pid));
Result result = ResultSuccess;
if (result.IsSuccess()) {
result = manager->IsAlbumMounted(AlbumStorage::Sd);
}
std::vector<ApplicationAlbumEntry> entries;
if (result.IsSuccess()) {
result =
manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time,
parameters.end_date_time, parameters.applet_resource_user_id);
}
if (!entries.empty()) {
ctx.WriteBuffer(entries);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push<u64>(entries.size());
}
} // namespace Service::Capture

View File

@@ -3,7 +3,6 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -20,18 +19,9 @@ public:
~IAlbumApplicationService() override;
private:
Result SetShimLibraryVersion(ShimLibraryVersion library_version,
ClientAppletResourceUserId aruid);
Result GetAlbumFileList0AafeAruidDeprecated(
Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time,
s64 end_posix_time, ClientAppletResourceUserId aruid,
OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries);
Result GetAlbumFileList3AaeAruid(
Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries);
void SetShimLibraryVersion(HLERequestContext& ctx);
void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx);
void GetAlbumFileList3AaeAruid(HLERequestContext& ctx);
std::shared_ptr<AlbumManager> manager = nullptr;
};

View File

@@ -122,14 +122,14 @@ struct RequestLayout {
u32 domain_interface_count;
};
template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetInRawDataSize() {
template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentRawDataSize() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(DataOffset);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
@@ -138,33 +138,9 @@ constexpr u32 GetInRawDataSize() {
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
return GetInRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
} else {
return GetInRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
}
}
}
template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetOutRawDataSize() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(DataOffset);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
using RawArgType = typename ArgType::Type;
constexpr size_t ArgAlign = alignof(RawArgType);
constexpr size_t ArgSize = sizeof(RawArgType);
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
return GetOutRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
} else {
return GetOutRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
}
}
}
@@ -189,7 +165,7 @@ constexpr RequestLayout GetNonDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = 0,
};
}
@@ -199,7 +175,7 @@ constexpr RequestLayout GetDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
};
}
@@ -209,7 +185,7 @@ constexpr RequestLayout GetNonDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
.cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = 0,
};
}
@@ -219,7 +195,7 @@ constexpr RequestLayout GetDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
.cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
};
}
@@ -283,7 +259,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
constexpr size_t BufferSize = sizeof(typename ArgType::Type);
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
@@ -324,7 +300,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(typename ArgType::Type);
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args).raw, 0, BufferSize);
@@ -361,15 +337,13 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
using RawArgType = decltype(std::get<ArgIndex>(args).raw);
constexpr size_t ArgAlign = alignof(RawArgType);
constexpr size_t ArgSize = sizeof(RawArgType);
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
static_assert(!std::is_pointer_v<ArgType>, "Output raw data must not be a pointer");
static_assert(!std::is_pointer_v<RawArgType>, "Output raw data must not be a pointer");
static_assert(std::is_trivially_copyable_v<RawArgType>, "Output raw data must be trivially copyable");
static_assert(std::is_trivially_copyable_v<decltype(std::get<ArgIndex>(args).raw)>, "Output raw data must be trivially copyable");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
@@ -394,7 +368,7 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ
return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(typename ArgType::Type);
constexpr size_t BufferSize = sizeof(ArgType);
ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {

View File

@@ -197,27 +197,32 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(
Result TimeZoneService::ToPosixTime(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time,
InRule rule) {
Out<u32> out_times_count,
Service::PSC::Time::CalendarTime& calendar_time, InRule rule) {
SCOPE_EXIT({
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times[0], out_times[1]);
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} "
"out_times_count={}",
calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
});
R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
R_RETURN(
m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
}
Result TimeZoneService::ToPosixTimeWithMyRule(
Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time) {
Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
Out<u32> out_times_count,
Service::PSC::Time::CalendarTime& calendar_time) {
SCOPE_EXIT({
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times[0], out_times[1]);
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} "
"out_times_count={}",
calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
});
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
calendar_time));
}
} // namespace Service::Glue::Time

View File

@@ -68,10 +68,12 @@ public:
Out<Service::PSC::Time::CalendarTime> out_calendar_time,
Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time);
Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time, InRule rule);
Out<u32> out_times_count, Service::PSC::Time::CalendarTime& calendar_time,
InRule rule);
Result ToPosixTimeWithMyRule(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time);
Out<u32> out_times_count,
Service::PSC::Time::CalendarTime& calendar_time);
private:
Core::System& m_system;

View File

@@ -8,7 +8,6 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
@@ -154,7 +153,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{104, &IHidServer::DeactivateNpad, "DeactivateNpad"},
{106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &IHidServer::DisconnectNpad, "DisconnectNpad"},
{108, C<&IHidServer::GetPlayerLedPattern>, "GetPlayerLedPattern"},
{108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"},
{109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
{120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
@@ -1137,39 +1136,19 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
Result IHidServer::GetPlayerLedPattern(Out<Core::HID::LedPattern> out_led_pattern,
Core::HID::NpadIdType npad_id) {
void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
Core::HID::LedPattern pattern{0, 0, 0, 0};
auto controller = GetResourceManager()->GetNpad();
const auto result = controller->GetLedPattern(npad_id, pattern);
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
switch (npad_id) {
case Core::HID::NpadIdType::Player1:
*out_led_pattern = Core::HID::LedPattern{1, 0, 0, 0};
R_SUCCEED();
case Core::HID::NpadIdType::Player2:
*out_led_pattern = Core::HID::LedPattern{1, 1, 0, 0};
R_SUCCEED();
case Core::HID::NpadIdType::Player3:
*out_led_pattern = Core::HID::LedPattern{1, 1, 1, 0};
R_SUCCEED();
case Core::HID::NpadIdType::Player4:
*out_led_pattern = Core::HID::LedPattern{1, 1, 1, 1};
R_SUCCEED();
case Core::HID::NpadIdType::Player5:
*out_led_pattern = Core::HID::LedPattern{1, 0, 0, 1};
R_SUCCEED();
case Core::HID::NpadIdType::Player6:
*out_led_pattern = Core::HID::LedPattern{1, 0, 1, 0};
R_SUCCEED();
case Core::HID::NpadIdType::Player7:
*out_led_pattern = Core::HID::LedPattern{1, 0, 1, 1};
R_SUCCEED();
case Core::HID::NpadIdType::Player8:
*out_led_pattern = Core::HID::LedPattern{0, 1, 1, 0};
R_SUCCEED();
default:
*out_led_pattern = Core::HID::LedPattern{0, 0, 0, 0};
R_SUCCEED();
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push(pattern.raw);
}
void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {

View File

@@ -3,9 +3,7 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
namespace Core {
class System;
@@ -68,8 +66,7 @@ private:
void DeactivateNpad(HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
void DisconnectNpad(HLERequestContext& ctx);
Result GetPlayerLedPattern(Out<Core::HID::LedPattern> out_led_pattern,
Core::HID::NpadIdType npad_id);
void GetPlayerLedPattern(HLERequestContext& ctx);
void ActivateNpadWithRevision(HLERequestContext& ctx);
void SetNpadJoyHoldType(HLERequestContext& ctx);
void GetNpadJoyHoldType(HLERequestContext& ctx);

View File

@@ -111,8 +111,7 @@ private:
R_RETURN(result);
}
Result UpdateLatest(Out<CharInfo> out_char_info, const CharInfo& char_info,
SourceFlag source_flag) {
Result UpdateLatest(Out<CharInfo> out_char_info, CharInfo& char_info, SourceFlag source_flag) {
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
R_RETURN(manager->UpdateLatest(metadata, *out_char_info, char_info, source_flag));
@@ -160,7 +159,7 @@ private:
R_RETURN(result);
}
Result UpdateLatest1(Out<StoreData> out_store_data, const StoreData& store_data,
Result UpdateLatest1(Out<StoreData> out_store_data, StoreData& store_data,
SourceFlag source_flag) {
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
R_UNLESS(is_system, ResultPermissionDenied);
@@ -244,7 +243,7 @@ private:
R_SUCCEED();
}
Result GetIndex(Out<s32> out_index, const CharInfo& char_info) {
Result GetIndex(Out<s32> out_index, CharInfo& char_info) {
LOG_DEBUG(Service_Mii, "called");
R_RETURN(manager->GetIndex(metadata, char_info, *out_index));
@@ -258,25 +257,25 @@ private:
R_SUCCEED();
}
Result Convert(Out<CharInfo> out_char_info, const Ver3StoreData& mii_v3) {
Result Convert(Out<CharInfo> out_char_info, Ver3StoreData& mii_v3) {
LOG_INFO(Service_Mii, "called");
R_RETURN(manager->ConvertV3ToCharInfo(*out_char_info, mii_v3));
}
Result ConvertCoreDataToCharInfo(Out<CharInfo> out_char_info, const CoreData& core_data) {
Result ConvertCoreDataToCharInfo(Out<CharInfo> out_char_info, CoreData& core_data) {
LOG_INFO(Service_Mii, "called");
R_RETURN(manager->ConvertCoreDataToCharInfo(*out_char_info, core_data));
}
Result ConvertCharInfoToCoreData(Out<CoreData> out_core_data, const CharInfo& char_info) {
Result ConvertCharInfoToCoreData(Out<CoreData> out_core_data, CharInfo& char_info) {
LOG_INFO(Service_Mii, "called");
R_RETURN(manager->ConvertCharInfoToCoreData(*out_core_data, char_info));
}
Result Append(const CharInfo& char_info) {
Result Append(CharInfo& char_info) {
LOG_INFO(Service_Mii, "called");
R_RETURN(manager->Append(metadata, char_info));

View File

@@ -83,9 +83,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
// Check if this memory block is heap.
if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
if (region_start + region_size == svc_mem_info.base_address) {
region_size += svc_mem_info.size;
} else if (svc_mem_info.size > region_size) {
if (svc_mem_info.size > region_size) {
region_size = svc_mem_info.size;
region_start = svc_mem_info.base_address;
}

View File

@@ -67,7 +67,10 @@ public:
const SyncpointManager& GetSyncpointManager() const;
struct Host1xDeviceFileData {
std::unordered_map<DeviceFD, u32> fd_to_id{};
std::deque<u32> syncpts_accumulated{};
u32 nvdec_next_id{};
u32 vic_next_id{};
};
Host1xDeviceFileData& Host1xDeviceFile();

View File

@@ -8,7 +8,6 @@
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
@@ -22,8 +21,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
case 0x1: {
auto& host1x_file = core.Host1xDeviceFile();
if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
}
return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
}
case 0x2:
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
case 0x3:
@@ -68,12 +72,15 @@ void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true);
sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
auto& host1x_file = core.Host1xDeviceFile();
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
system.SetNVDECActive(false);
auto it = sessions.find(fd);
if (it != sessions.end()) {

View File

@@ -55,9 +55,8 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
NvCore::ChannelType channel_type_)
: nvdevice{system_}, host1x{system_.Host1x()}, core{core_},
syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
channel_type{channel_type_} {
: nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()},
nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {
auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
if (syncpts_accumulated.empty()) {
channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
@@ -96,24 +95,24 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU();
auto* session = core.GetSession(sessions[fd]);
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i];
fence_thresholds[i] =
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
if (gpu.UseNvdec()) {
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i];
fence_thresholds[i] =
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
}
}
for (const auto& cmd_buffer : command_buffers) {
const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
Core::Memory::GuestMemoryFlags::SafeRead>
cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
cmd_buffer.word_count);
host1x.PushEntries(fd, std::move(cmdlist));
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
}
// Some games expect command_buffers to be written back
offset = 0;
offset += WriteVectors(data, command_buffers, offset);

View File

@@ -119,7 +119,6 @@ protected:
Kernel::KEvent* QueryEvent(u32 event_id) override;
Tegra::Host1x::Host1x& host1x;
u32 channel_syncpoint;
s32_le nvmap_fd{};
u32_le submit_timeout{};

View File

@@ -7,7 +7,6 @@
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
@@ -22,8 +21,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
case 0x1: {
auto& host1x_file = core.Host1xDeviceFile();
if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
}
return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
}
case 0x2:
return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
case 0x3:
@@ -66,11 +70,14 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::VIC, channel_syncpoint);
}
void nvhost_vic::OnClose(DeviceFD fd) {
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::VIC);
auto& host1x_file = core.Host1xDeviceFile();
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
sessions.erase(fd);
}

View File

@@ -189,7 +189,7 @@ struct fmt::formatter<Service::PSC::Time::SteadyClockTimePoint> : fmt::formatter
template <typename FormatContext>
auto format(const Service::PSC::Time::SteadyClockTimePoint& time_point,
FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "[time_point={}]", time_point.time_point);
return fmt::format_to(ctx.out(), "time_point={}", time_point.time_point);
}
};
@@ -197,7 +197,7 @@ template <>
struct fmt::formatter<Service::PSC::Time::SystemClockContext> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(const Service::PSC::Time::SystemClockContext& context, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "[offset={} steady_time_point={}]", context.offset,
return fmt::format_to(ctx.out(), "offset={} steady_time_point={}", context.offset,
context.steady_time_point.time_point);
}
};
@@ -206,9 +206,8 @@ template <>
struct fmt::formatter<Service::PSC::Time::CalendarTime> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(const Service::PSC::Time::CalendarTime& calendar, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "[{:02}/{:02}/{:04} {:02}:{:02}:{:02}]", calendar.day,
calendar.month, calendar.year, calendar.hour, calendar.minute,
calendar.second);
return fmt::format_to(ctx.out(), "{}/{}/{} {}:{}:{}", calendar.day, calendar.month,
calendar.year, calendar.hour, calendar.minute, calendar.second);
}
};
@@ -218,7 +217,7 @@ struct fmt::formatter<Service::PSC::Time::CalendarAdditionalInfo>
template <typename FormatContext>
auto format(const Service::PSC::Time::CalendarAdditionalInfo& additional,
FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "[weekday={} yearday={} name={} is_dst={} ut_offset={}]",
return fmt::format_to(ctx.out(), "weekday={} yearday={} name={} is_dst={} ut_offset={}",
additional.day_of_week, additional.day_of_year,
additional.name.data(), additional.is_dst, additional.ut_offset);
}
@@ -228,7 +227,8 @@ template <>
struct fmt::formatter<Service::PSC::Time::LocationName> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(const Service::PSC::Time::LocationName& name, FormatContext& ctx) const {
return formatter<string_view>::format(name.data(), ctx);
std::string_view n{name.data(), name.size()};
return formatter<string_view>::format(n, ctx);
}
};
@@ -236,7 +236,8 @@ template <>
struct fmt::formatter<Service::PSC::Time::RuleVersion> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(const Service::PSC::Time::RuleVersion& version, FormatContext& ctx) const {
return formatter<string_view>::format(version.data(), ctx);
std::string_view v{version.data(), version.size()};
return formatter<string_view>::format(v, ctx);
}
};
@@ -246,11 +247,10 @@ struct fmt::formatter<Service::PSC::Time::ClockSnapshot> : fmt::formatter<fmt::s
auto format(const Service::PSC::Time::ClockSnapshot& snapshot, FormatContext& ctx) const {
return fmt::format_to(
ctx.out(),
"[user_context={} network_context={} user_time={} network_time={} "
"user_calendar_time={} "
"user_context={} network_context={} user_time={} network_time={} user_calendar_time={} "
"network_calendar_time={} user_calendar_additional_time={} "
"network_calendar_additional_time={} steady_clock_time_point={} location={} "
"is_automatic_correction_enabled={} type={}]",
"is_automatic_correction_enabled={} type={}",
snapshot.user_context, snapshot.network_context, snapshot.user_time,
snapshot.network_time, snapshot.user_calendar_time, snapshot.network_calendar_time,
snapshot.user_calendar_additional_time, snapshot.network_calendar_additional_time,
@@ -266,7 +266,7 @@ struct fmt::formatter<Service::PSC::Time::ContinuousAdjustmentTimePoint>
auto format(const Service::PSC::Time::ContinuousAdjustmentTimePoint& time_point,
FormatContext& ctx) const {
return fmt::format_to(ctx.out(),
"[rtc_offset={} diff_scale={} shift_amount={} lower={} upper={}]",
"rtc_offset={} diff_scale={} shift_amount={} lower={} upper={}",
time_point.rtc_offset, time_point.diff_scale, time_point.shift_amount,
time_point.lower, time_point.upper);
}

View File

@@ -120,8 +120,11 @@ Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& c
context, context.steady_time_point.clock_source_id.RawString(), accuracy);
// TODO this is a hack! The network clock should be updated independently, from the ntc service
// and maybe elsewhere. We do not do that, so fix the clock to the local clock.
m_local_system_clock.GetContext(context);
// and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
// to avoid it being stuck at 0.
if (context == Service::PSC::Time::SystemClockContext{}) {
m_local_system_clock.GetContext(context);
}
m_network_system_clock.SetContextWriter(m_network_system_context_writer);
m_network_system_clock.Initialize(context, accuracy);
@@ -135,6 +138,13 @@ Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correctio
LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}",
automatic_correction, time_point, time_point.clock_source_id.RawString());
// TODO this is a hack! The user clock should be updated independently, from the ntc service
// and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
// to avoid it being stuck at 0.
if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) {
m_local_system_clock.GetCurrentTimePoint(time_point);
}
m_user_system_clock.SetAutomaticCorrection(automatic_correction);
m_user_system_clock.SetTimePointAndSignal(time_point);
m_user_system_clock.SetInitialized();

View File

@@ -140,11 +140,11 @@ Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary)
R_RETURN(ParseBinaryImpl(out_rule, binary));
}
Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
const CalendarTime& calendar, const Tz::Rule& rule) {
Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, u32 out_times_count,
CalendarTime& calendar, const Tz::Rule& rule) {
std::scoped_lock l{m_mutex};
auto res = ToPosixTimeImpl(out_count, out_times, out_times_max_count, calendar, rule, -1);
auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1);
if (res != ResultSuccess) {
if (res == ResultTimeZoneNotFound) {
@@ -158,10 +158,10 @@ Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, size_t ou
}
Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times,
size_t out_times_max_count, const CalendarTime& calendar) {
u32 out_times_count, CalendarTime& calendar) {
std::scoped_lock l{m_mutex};
auto res = ToPosixTimeImpl(out_count, out_times, out_times_max_count, calendar, m_my_rule, -1);
auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1);
if (res != ResultSuccess) {
if (res == ResultTimeZoneNotFound) {
@@ -212,23 +212,20 @@ Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time,
R_SUCCEED();
}
Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times,
size_t out_times_max_count, const CalendarTime& calendar,
const Tz::Rule& rule, s32 is_dst) {
Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 out_times_count,
CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst) {
R_TRY(ValidateRule(rule));
CalendarTime local_calendar{calendar};
local_calendar.month -= 1;
local_calendar.year -= 1900;
calendar.month -= 1;
calendar.year -= 1900;
Tz::CalendarTimeInternal internal{
.tm_sec = local_calendar.second,
.tm_min = local_calendar.minute,
.tm_hour = local_calendar.hour,
.tm_mday = local_calendar.day,
.tm_mon = local_calendar.month,
.tm_year = local_calendar.year,
.tm_sec = calendar.second,
.tm_min = calendar.minute,
.tm_hour = calendar.hour,
.tm_mday = calendar.day,
.tm_mon = calendar.month,
.tm_year = calendar.year,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = is_dst,
@@ -246,9 +243,9 @@ Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times,
R_RETURN(ResultTimeZoneNotFound);
}
if (internal.tm_sec != local_calendar.second || internal.tm_min != local_calendar.minute ||
internal.tm_hour != local_calendar.hour || internal.tm_mday != local_calendar.day ||
internal.tm_mon != local_calendar.month || internal.tm_year != local_calendar.year) {
if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute ||
internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day ||
internal.tm_mon != calendar.month || internal.tm_year != calendar.year) {
R_RETURN(ResultTimeZoneNotFound);
}
@@ -257,7 +254,7 @@ Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times,
}
out_times[0] = time;
if (out_times_max_count < 2) {
if (out_times_count < 2) {
out_count = 1;
R_SUCCEED();
}

View File

@@ -38,18 +38,18 @@ public:
CalendarAdditionalInfo& calendar_additional, s64 time);
Result ParseBinary(LocationName& name, std::span<const u8> binary);
Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
const CalendarTime& calendar, const Tz::Rule& rule);
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times,
size_t out_times_max_count, const CalendarTime& calendar);
Result ToPosixTime(u32& out_count, std::span<s64> out_times, u32 out_times_count,
CalendarTime& calendar, const Tz::Rule& rule);
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times, u32 out_times_count,
CalendarTime& calendar);
private:
Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary);
Result ToCalendarTimeImpl(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time,
const Tz::Rule& rule);
Result ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
const CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst);
Result ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 out_times_count,
CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst);
bool m_initialized{};
std::recursive_mutex m_mutex;

View File

@@ -138,28 +138,32 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_
Result TimeZoneService::ToPosixTime(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time, InRule rule) {
Out<u32> out_times_count, CalendarTime& calendar_time,
InRule rule) {
SCOPE_EXIT({
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times[0], out_times[1]);
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} "
"out_times_count={}",
calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
});
R_RETURN(
m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule));
m_time_zone.ToPosixTime(*out_count, out_times, *out_times_count, calendar_time, *rule));
}
Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time) {
Out<u32> out_times_count,
CalendarTime& calendar_time) {
SCOPE_EXIT({
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times[0], out_times[1]);
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} "
"out_times_count={}",
calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
});
R_RETURN(
m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time));
m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, *out_times_count, calendar_time));
}
} // namespace Service::PSC::Time

View File

@@ -50,10 +50,10 @@ public:
Result ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time,
Out<CalendarAdditionalInfo> out_additional_info, s64 time);
Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time, InRule rule);
Out<u32> out_times_count, CalendarTime& calendar_time, InRule rule);
Result ToPosixTimeWithMyRule(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time);
Out<u32> out_times_count, CalendarTime& calendar_time);
private:
Core::System& m_system;

View File

@@ -45,7 +45,7 @@ SystemSettings DefaultSystemSettings() {
.console_sleep_plan = ConsoleSleepPlan::Sleep1Hour,
};
settings.device_time_zone_location_name = Service::PSC::Time::LocationName{"UTC"};
settings.device_time_zone_location_name = {"UTC"};
settings.user_system_clock_automatic_correction_enabled = true;
settings.primary_album_storage = PrimaryAlbumStorage::SdCard;

View File

@@ -25,7 +25,7 @@
namespace Service::Set {
namespace {
constexpr u32 SETTINGS_VERSION{3u};
constexpr u32 SETTINGS_VERSION{2u};
constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't');
struct SettingsHeader {
u64 magic;

View File

@@ -24,7 +24,6 @@ enum class Errno : u32 {
CONNRESET = 104,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
INPROGRESS = 115,
};

View File

@@ -25,8 +25,6 @@ Errno Translate(Network::Errno value) {
return Errno::MFILE;
case Network::Errno::PIPE:
return Errno::PIPE;
case Network::Errno::CONNREFUSED:
return Errno::CONNREFUSED;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
case Network::Errno::TIMEDOUT:

View File

@@ -693,23 +693,20 @@ std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
const bool wait_for_accept = !is_non_blocking;
if (wait_for_accept) {
std::vector<WSAPOLLFD> host_pollfds{
WSAPOLLFD{fd, POLLIN, 0},
WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
};
std::vector<WSAPOLLFD> host_pollfds{
WSAPOLLFD{fd, POLLIN, 0},
WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
};
while (true) {
const int pollres =
WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
if (host_pollfds[1].revents != 0) {
// Interrupt signaled before a client could be accepted, break
return {AcceptResult{}, Errno::AGAIN};
}
if (pollres > 0) {
break;
}
while (true) {
const int pollres =
WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
if (host_pollfds[1].revents != 0) {
// Interrupt signaled before a client could be accepted, break
return {AcceptResult{}, Errno::AGAIN};
}
if (pollres > 0) {
break;
}
}
@@ -916,7 +913,6 @@ Errno Socket::SetRcvTimeo(u32 value) {
Errno Socket::SetNonBlock(bool enable) {
if (EnableNonBlock(fd, enable)) {
is_non_blocking = enable;
return Errno::SUCCESS;
}
return GetAndLogLastError();

View File

@@ -166,9 +166,6 @@ public:
bool IsOpened() const override;
void HandleProxyPacket(const ProxyPacket& packet) override;
private:
bool is_non_blocking = false;
};
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);

View File

@@ -1091,6 +1091,20 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
[&] { rasterizer = true; });
if (rasterizer) {
impl->InvalidateGPUMemory(ptr, size);
const auto type = impl->current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type();
if (type == Common::PageType::RasterizerCachedMemory) {
// Check if device mapped. If not, this bugged and we can unmark.
DAddr addr{};
Common::ScratchBuffer<u32> buffer;
impl->gpu_device_memory->ApplyOpOnPointer(ptr, buffer,
[&](DAddr address) { addr = address; });
if (addr == 0) {
LOG_ERROR(HW_Memory, "Fixing unmapped cached region {:#x}", GetInteger(vaddr));
impl->RasterizerMarkRegionCached(GetInteger(vaddr), size, false);
}
}
}
#ifdef __linux__

View File

@@ -64,8 +64,6 @@ public:
Memory(Memory&&) = default;
Memory& operator=(Memory&&) = delete;
static constexpr bool HAS_FLUSH_INVALIDATION = false;
/**
* Resets the state of the Memory system.
*/

View File

@@ -96,7 +96,6 @@ add_library(hid_core STATIC
resources/system_buttons/home_button.h
resources/system_buttons/sleep_button.cpp
resources/system_buttons/sleep_button.h
resources/system_buttons/system_button_types.h
resources/touch_screen/gesture.cpp
resources/touch_screen/gesture.h
resources/touch_screen/gesture_types.h

View File

@@ -422,10 +422,7 @@ struct NpadPowerInfo {
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
struct LedPattern {
LedPattern() {
raw = 0;
}
LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
position2.Assign(light2);
position3.Assign(light3);
@@ -440,16 +437,6 @@ struct LedPattern {
};
};
struct SleepButtonState {
union {
u64 raw{};
// Buttons
BitField<0, 1, u64> sleep;
};
};
static_assert(sizeof(SleepButtonState) == 0x8, "SleepButtonState has incorrect size.");
struct HomeButtonState {
union {
u64 raw{};

View File

@@ -956,6 +956,17 @@ Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
return ResultSuccess;
}
Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return ResultInvalidNpadId;
}
const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
const auto& controller = GetControllerFromNpadIdType(aruid, npad_id).device;
pattern = controller->GetLedPattern();
return ResultSuccess;
}
Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
Core::HID::NpadIdType npad_id) const {
std::scoped_lock lock{mutex};

View File

@@ -97,6 +97,8 @@ public:
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle);
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
Result IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
Core::HID::NpadIdType npad_id) const;
Result EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,

View File

@@ -12,7 +12,6 @@
#include "hid_core/resources/mouse/mouse_types.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/ring_lifo.h"
#include "hid_core/resources/system_buttons/system_button_types.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID {
@@ -76,24 +75,24 @@ static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
// This is nn::hid::detail::HomeButtonSharedMemoryFormat
struct HomeButtonSharedMemoryFormat {
Lifo<HomeButtonState, HidEntryCount> home_lifo{};
INSERT_PADDING_BYTES(0x48);
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
"HomeButtonSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::SleepButtonSharedMemoryFormat
struct SleepButtonSharedMemoryFormat {
Lifo<SleepButtonState, HidEntryCount> sleep_lifo{};
INSERT_PADDING_BYTES(0x48);
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
"SleepButtonSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
struct CaptureButtonSharedMemoryFormat {
Lifo<CaptureButtonState, HidEntryCount> capture_lifo{};
INSERT_PADDING_BYTES(0x48);
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
"CaptureButtonSharedMemoryFormat is an invalid size");

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/capture_button.h"
@@ -19,6 +17,10 @@ void CaptureButton::OnInit() {}
void CaptureButton::OnRelease() {}
void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
@@ -27,21 +29,11 @@ void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
auto& shared_memory = data->shared_memory_format->capture_button;
if (!IsControllerActivated()) {
shared_memory.capture_lifo.buffer_count = 0;
shared_memory.capture_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory.capture_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
auto* controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
next_state.buttons.raw = controller->GetHomeButtons().raw;
shared_memory.capture_lifo.WriteNextEntry(next_state);
auto& header = data->shared_memory_format->capture_button.header;
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
}
} // namespace Service::HID

View File

@@ -4,7 +4,6 @@
#pragma once
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"
namespace Service::HID {
@@ -23,6 +22,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
CaptureButtonState next_state{};
bool smart_update{};
};
} // namespace Service::HID

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/home_button.h"
@@ -19,6 +17,10 @@ void HomeButton::OnInit() {}
void HomeButton::OnRelease() {}
void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
@@ -27,21 +29,11 @@ void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
auto& shared_memory = data->shared_memory_format->home_button;
if (!IsControllerActivated()) {
shared_memory.home_lifo.buffer_count = 0;
shared_memory.home_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory.home_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
auto* controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
next_state.buttons.raw = controller->GetHomeButtons().raw;
shared_memory.home_lifo.WriteNextEntry(next_state);
auto& header = data->shared_memory_format->home_button.header;
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
}
} // namespace Service::HID

View File

@@ -4,7 +4,6 @@
#pragma once
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"
namespace Service::HID {
@@ -23,6 +22,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
HomeButtonState next_state{};
bool smart_update{};
};
} // namespace Service::HID

View File

@@ -17,6 +17,10 @@ void SleepButton::OnInit() {}
void SleepButton::OnRelease() {}
void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
@@ -25,20 +29,11 @@ void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
auto& shared_memory = data->shared_memory_format->sleep_button;
if (!IsControllerActivated()) {
shared_memory.sleep_lifo.buffer_count = 0;
shared_memory.sleep_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory.sleep_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.buttons.raw = 0;
shared_memory.sleep_lifo.WriteNextEntry(next_state);
auto& header = data->shared_memory_format->capture_button.header;
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
}
} // namespace Service::HID

View File

@@ -4,7 +4,6 @@
#pragma once
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"
namespace Service::HID {
@@ -23,6 +22,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
SleepButtonState next_state{};
bool smart_update{};
};
} // namespace Service::HID

View File

@@ -1,31 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "hid_core/hid_types.h"
namespace Service::HID {
// This is nn::hid::system::SleepButtonState
struct SleepButtonState {
s64 sampling_number{};
Core::HID::SleepButtonState buttons;
};
static_assert(sizeof(SleepButtonState) == 0x10, "SleepButtonState is an invalid size");
// This is nn::hid::system::HomeButtonState
struct HomeButtonState {
s64 sampling_number{};
Core::HID::HomeButtonState buttons;
};
static_assert(sizeof(HomeButtonState) == 0x10, "HomeButtonState is an invalid size");
// This is nn::hid::system::SleepButtonState
struct CaptureButtonState {
s64 sampling_number{};
Core::HID::CaptureButtonState buttons;
};
static_assert(sizeof(CaptureButtonState) == 0x10, "CaptureButtonState is an invalid size");
} // namespace Service::HID

View File

@@ -28,7 +28,7 @@ void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
auto& header = data->shared_memory_format->unique_pad.header;
auto& header = data->shared_memory_format->capture_button.header;
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;

View File

@@ -12,6 +12,11 @@ namespace Shader::Backend::SPIRV {
namespace {
class ImageOperands {
public:
[[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
[[maybe_unused]] static constexpr bool ImageGatherOffsetAllowed = true;
[[maybe_unused]] static constexpr bool ImageFetchOffsetAllowed = false;
[[maybe_unused]] static constexpr bool ImageGradientOffsetAllowed = false;
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
Id lod, const IR::Value& offset) {
if (has_bias) {
@@ -22,7 +27,7 @@ public:
const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
Add(spv::ImageOperandsMask::Lod, lod_value);
}
AddOffset(ctx, offset);
AddOffset(ctx, offset, ImageSampleOffsetAllowed);
if (has_lod_clamp) {
const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
@@ -55,20 +60,17 @@ public:
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
}
explicit ImageOperands(Id offset, Id lod, Id ms) {
explicit ImageOperands(Id lod, Id ms) {
if (Sirit::ValidId(lod)) {
Add(spv::ImageOperandsMask::Lod, lod);
}
if (Sirit::ValidId(offset)) {
Add(spv::ImageOperandsMask::Offset, offset);
}
if (Sirit::ValidId(ms)) {
Add(spv::ImageOperandsMask::Sample, ms);
}
}
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
u32 num_derivatives, Id offset, Id lod_clamp) {
u32 num_derivatives, const IR::Value& offset, Id lod_clamp) {
if (!Sirit::ValidId(derivatives)) {
throw LogicError("Derivatives must be present");
}
@@ -83,16 +85,14 @@ public:
const Id derivatives_Y{ctx.OpCompositeConstruct(
ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
if (Sirit::ValidId(offset)) {
Add(spv::ImageOperandsMask::Offset, offset);
}
AddOffset(ctx, offset, ImageGradientOffsetAllowed);
if (has_lod_clamp) {
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
}
}
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
Id offset, Id lod_clamp) {
const IR::Value& offset, Id lod_clamp) {
if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
throw LogicError("Derivatives must be present");
}
@@ -111,9 +111,7 @@ public:
const Id derivatives_id2{ctx.OpCompositeConstruct(
ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
if (Sirit::ValidId(offset)) {
Add(spv::ImageOperandsMask::Offset, offset);
}
AddOffset(ctx, offset, ImageGradientOffsetAllowed);
if (has_lod_clamp) {
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
}
@@ -132,7 +130,7 @@ public:
}
private:
void AddOffset(EmitContext& ctx, const IR::Value& offset) {
void AddOffset(EmitContext& ctx, const IR::Value& offset, bool runtime_offset_allowed) {
if (offset.IsEmpty()) {
return;
}
@@ -165,7 +163,9 @@ private:
break;
}
}
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
if (runtime_offset_allowed) {
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
}
}
void Add(spv::ImageOperandsMask new_mask, Id value) {
@@ -311,6 +311,37 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
return coords;
}
}
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
Id offset) {
if (!Sirit::ValidId(offset)) {
return;
}
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D:
case TextureType::ColorArray1D: {
result_type = ctx.U32[1];
break;
}
case TextureType::Color2D:
case TextureType::Color2DRect:
case TextureType::ColorArray2D: {
result_type = ctx.U32[2];
break;
}
case TextureType::Color3D: {
result_type = ctx.U32[3];
break;
}
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
return;
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -496,6 +527,7 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
}
@@ -503,7 +535,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
// This image is multisampled, lod must be implicit
lod = Id{};
}
const ImageOperands operands(offset, lod, ms);
const ImageOperands operands(lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
}
@@ -548,13 +580,13 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
}
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, Id offset, Id lod_clamp) {
Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const auto operands =
info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset,
lod_clamp);
const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());

View File

@@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, Id offset, Id lod_clamp);
Id derivatives, const IR::Value& offset, Id lod_clamp);
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);

View File

@@ -58,8 +58,8 @@ add_library(video_core STATIC
framebuffer_config.h
fsr.cpp
fsr.h
host1x/codecs/decoder.cpp
host1x/codecs/decoder.h
host1x/codecs/codec.cpp
host1x/codecs/codec.h
host1x/codecs/h264.cpp
host1x/codecs/h264.h
host1x/codecs/vp8.cpp
@@ -78,6 +78,8 @@ add_library(video_core STATIC
host1x/nvdec.cpp
host1x/nvdec.h
host1x/nvdec_common.h
host1x/sync_manager.cpp
host1x/sync_manager.h
host1x/syncpoint_manager.cpp
host1x/syncpoint_manager.h
host1x/vic.cpp
@@ -300,7 +302,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
add_dependencies(video_core host_shaders)
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
target_link_libraries(video_core PRIVATE sirit Vulkan::Headers Vulkan::UtilityHeaders GPUOpen::VulkanMemoryAllocator)
target_link_libraries(video_core PRIVATE sirit Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
if (ENABLE_NSIGHT_AFTERMATH)
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})

View File

@@ -2,130 +2,136 @@
// SPDX-License-Identifier: MIT
#include <bit>
#include "common/thread.h"
#include "core/core.h"
#include "video_core/cdma_pusher.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/control.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/nvdec.h"
#include "video_core/host1x/nvdec_common.h"
#include "video_core/host1x/sync_manager.h"
#include "video_core/host1x/vic.h"
#include "video_core/memory_manager.h"
namespace Tegra {
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
: host1x{host1x_}, memory_manager{host1x.GMMU()},
host_processor{std::make_unique<Host1x::Control>(host1x_)}, current_class{
static_cast<ChClassId>(id)} {
thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); });
}
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_)
: host1x{host1x_}, nvdec_processor(std::make_shared<Host1x::Nvdec>(host1x)),
vic_processor(std::make_unique<Host1x::Vic>(host1x, nvdec_processor)),
host1x_processor(std::make_unique<Host1x::Control>(host1x)),
sync_manager(std::make_unique<Host1x::SyncptIncrManager>(host1x)) {}
CDmaPusher::~CDmaPusher() = default;
void CDmaPusher::ProcessEntries(std::stop_token stop_token) {
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
ChCommandHeaderList command_list{host1x.System().ApplicationMemory(), 0, 0};
u32 count{};
u32 method_offset{};
u32 mask{};
bool incrementing{};
while (!stop_token.stop_requested()) {
{
std::unique_lock l{command_mutex};
Common::CondvarWait(command_cv, l, stop_token,
[this]() { return command_lists.size() > 0; });
if (stop_token.stop_requested()) {
return;
void CDmaPusher::ProcessEntries(ChCommandHeaderList&& entries) {
for (const auto& value : entries) {
if (mask != 0) {
const auto lbs = static_cast<u32>(std::countr_zero(mask));
mask &= ~(1U << lbs);
ExecuteCommand(offset + lbs, value.raw);
continue;
} else if (count != 0) {
--count;
ExecuteCommand(offset, value.raw);
if (incrementing) {
++offset;
}
command_list = std::move(command_lists.front());
command_lists.pop_front();
continue;
}
size_t i = 0;
for (const auto value : command_list) {
i++;
if (mask != 0) {
const auto lbs = static_cast<u32>(std::countr_zero(mask));
mask &= ~(1U << lbs);
ExecuteCommand(method_offset + lbs, value.raw);
continue;
} else if (count != 0) {
--count;
ExecuteCommand(method_offset, value.raw);
if (incrementing) {
++method_offset;
}
continue;
}
const auto mode = value.submission_mode.Value();
switch (mode) {
case ChSubmissionMode::SetClass: {
mask = value.value & 0x3f;
method_offset = value.method_offset;
current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff);
break;
}
case ChSubmissionMode::Incrementing:
case ChSubmissionMode::NonIncrementing:
count = value.value;
method_offset = value.method_offset;
incrementing = mode == ChSubmissionMode::Incrementing;
break;
case ChSubmissionMode::Mask:
mask = value.value;
method_offset = value.method_offset;
break;
case ChSubmissionMode::Immediate: {
const u32 data = value.value & 0xfff;
method_offset = value.method_offset;
ExecuteCommand(method_offset, data);
break;
}
default:
LOG_ERROR(HW_GPU, "Bad command at index {} (bytes 0x{:X}), buffer size {}", i - 1,
(i - 1) * sizeof(u32), command_list.size());
UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!",
static_cast<u32>(mode));
break;
}
const auto mode = value.submission_mode.Value();
switch (mode) {
case ChSubmissionMode::SetClass: {
mask = value.value & 0x3f;
offset = value.method_offset;
current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff);
break;
}
case ChSubmissionMode::Incrementing:
case ChSubmissionMode::NonIncrementing:
count = value.value;
offset = value.method_offset;
incrementing = mode == ChSubmissionMode::Incrementing;
break;
case ChSubmissionMode::Mask:
mask = value.value;
offset = value.method_offset;
break;
case ChSubmissionMode::Immediate: {
const u32 data = value.value & 0xfff;
offset = value.method_offset;
ExecuteCommand(offset, data);
break;
}
default:
UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!", static_cast<u32>(mode));
break;
}
}
}
void CDmaPusher::ExecuteCommand(u32 method, u32 arg) {
void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
switch (current_class) {
case ChClassId::Control:
LOG_TRACE(Service_NVDRV, "Class {} method 0x{:X} arg 0x{:X}",
static_cast<u32>(current_class), method, arg);
host_processor->ProcessMethod(static_cast<Host1x::Control::Method>(method), arg);
break;
default:
thi_regs.reg_array[method] = arg;
switch (static_cast<ThiMethod>(method)) {
case ChClassId::NvDec:
ThiStateWrite(nvdec_thi_state, offset, data);
switch (static_cast<ThiMethod>(offset)) {
case ThiMethod::IncSyncpt: {
const auto syncpoint_id = static_cast<u32>(arg & 0xFF);
[[maybe_unused]] const auto cond = static_cast<u32>((arg >> 8) & 0xFF);
LOG_TRACE(Service_NVDRV, "Class {} IncSyncpt Method, syncpt {} cond {}",
static_cast<u32>(current_class), syncpoint_id, cond);
auto& syncpoint_manager = host1x.GetSyncpointManager();
syncpoint_manager.IncrementGuest(syncpoint_id);
syncpoint_manager.IncrementHost(syncpoint_id);
LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
const auto syncpoint_id = static_cast<u32>(data & 0xFF);
const auto cond = static_cast<u32>((data >> 8) & 0xFF);
if (cond == 0) {
sync_manager->Increment(syncpoint_id);
} else {
sync_manager->SignalDone(
sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
}
break;
}
case ThiMethod::SetMethod1:
LOG_TRACE(Service_NVDRV, "Class {} method 0x{:X} arg 0x{:X}",
static_cast<u32>(current_class), static_cast<u32>(thi_regs.method_0), arg);
ProcessMethod(thi_regs.method_0, arg);
LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
static_cast<u32>(nvdec_thi_state.method_0));
nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data);
break;
default:
break;
}
break;
case ChClassId::GraphicsVic:
ThiStateWrite(vic_thi_state, static_cast<u32>(state_offset), {data});
switch (static_cast<ThiMethod>(state_offset)) {
case ThiMethod::IncSyncpt: {
LOG_DEBUG(Service_NVDRV, "VIC Class IncSyncpt Method");
const auto syncpoint_id = static_cast<u32>(data & 0xFF);
const auto cond = static_cast<u32>((data >> 8) & 0xFF);
if (cond == 0) {
sync_manager->Increment(syncpoint_id);
} else {
sync_manager->SignalDone(
sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
}
break;
}
case ThiMethod::SetMethod1:
LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
static_cast<u32>(vic_thi_state.method_0), data);
vic_processor->ProcessMethod(static_cast<Host1x::Vic::Method>(vic_thi_state.method_0),
data);
break;
default:
break;
}
break;
case ChClassId::Control:
// This device is mainly for syncpoint synchronization
LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
host1x_processor->ProcessMethod(static_cast<Host1x::Control::Method>(offset), data);
break;
default:
UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
break;
}
}
void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, u32 argument) {
u8* const offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset;
std::memcpy(offset_ptr, &argument, sizeof(u32));
}
} // namespace Tegra

View File

@@ -3,18 +3,12 @@
#pragma once
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/polyfill_thread.h"
#include "core/memory.h"
namespace Tegra {
@@ -68,31 +62,23 @@ struct ChCommand {
std::vector<u32> arguments;
};
using ChCommandHeaderList =
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader, Core::Memory::GuestMemoryFlags::SafeRead>;
using ChCommandHeaderList = std::vector<ChCommandHeader>;
struct ThiRegisters {
static constexpr std::size_t NUM_REGS = 0x20;
union {
struct {
u32_le increment_syncpt;
INSERT_PADDING_WORDS_NOINIT(1);
u32_le increment_syncpt_error;
u32_le ctx_switch_incremement_syncpt;
INSERT_PADDING_WORDS_NOINIT(4);
u32_le ctx_switch;
INSERT_PADDING_WORDS_NOINIT(1);
u32_le ctx_syncpt_eof;
INSERT_PADDING_WORDS_NOINIT(5);
u32_le method_0;
u32_le method_1;
INSERT_PADDING_WORDS_NOINIT(12);
u32_le int_status;
u32_le int_mask;
};
std::array<u32, NUM_REGS> reg_array;
};
u32_le increment_syncpt{};
INSERT_PADDING_WORDS(1);
u32_le increment_syncpt_error{};
u32_le ctx_switch_incremement_syncpt{};
INSERT_PADDING_WORDS(4);
u32_le ctx_switch{};
INSERT_PADDING_WORDS(1);
u32_le ctx_syncpt_eof{};
INSERT_PADDING_WORDS(5);
u32_le method_0{};
u32_le method_1{};
INSERT_PADDING_WORDS(12);
u32_le int_status{};
u32_le int_mask{};
};
enum class ThiMethod : u32 {
@@ -103,39 +89,32 @@ enum class ThiMethod : u32 {
class CDmaPusher {
public:
CDmaPusher() = delete;
virtual ~CDmaPusher();
explicit CDmaPusher(Host1x::Host1x& host1x);
~CDmaPusher();
void PushEntries(ChCommandHeaderList&& entries) {
std::scoped_lock l{command_mutex};
command_lists.push_back(std::move(entries));
command_cv.notify_one();
}
protected:
explicit CDmaPusher(Host1x::Host1x& host1x, s32 id);
virtual void ProcessMethod(u32 method, u32 arg) = 0;
Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
/// Process the command entry
void ProcessEntries(ChCommandHeaderList&& entries);
private:
/// Process the command entry
void ProcessEntries(std::stop_token stop_token);
/// Invoke command class devices to execute the command based on the current state
void ExecuteCommand(u32 state_offset, u32 data);
std::unique_ptr<Host1x::Control> host_processor;
/// Write arguments value to the ThiRegisters member at the specified offset
void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument);
std::mutex command_mutex;
std::condition_variable_any command_cv;
std::deque<ChCommandHeaderList> command_lists;
std::jthread thread;
Host1x::Host1x& host1x;
std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
std::unique_ptr<Tegra::Host1x::Vic> vic_processor;
std::unique_ptr<Tegra::Host1x::Control> host1x_processor;
std::unique_ptr<Host1x::SyncptIncrManager> sync_manager;
ChClassId current_class{};
ThiRegisters vic_thi_state{};
ThiRegisters nvdec_thi_state{};
ThiRegisters thi_regs{};
ChClassId current_class;
u32 count{};
u32 offset{};
u32 mask{};
bool incrementing{};
};
} // namespace Tegra

View File

@@ -111,20 +111,6 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt
}
}
template <bool unpack>
void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t extent_x,
size_t extent_y, u32 pitch, u32 x0, u32 y0, size_t bpp) {
const size_t base_offset = x0 * bpp;
const size_t copy_size = extent_x * bpp;
for (size_t y = 0; y < extent_y; y++) {
const size_t first_offset = (y + y0) * pitch + base_offset;
const size_t second_offset = y * extent_x * bpp;
u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
std::memcpy(write_to, read_from, copy_size);
}
}
} // namespace
struct SoftwareBlitEngine::BlitEngineImpl {
@@ -152,6 +138,19 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
}
return static_cast<size_t>(surface.pitch * surface.height);
};
const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
std::span<u8> output, u32 extent_x, u32 extent_y,
u32 pitch, u32 x0, u32 y0, size_t bpp) {
const size_t base_offset = x0 * bpp;
const size_t copy_size = extent_x * bpp;
for (u32 y = y0; y < extent_y; y++) {
const size_t first_offset = y * pitch + base_offset;
const size_t second_offset = y * extent_x * bpp;
u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
std::memcpy(write_to, read_from, copy_size);
}
};
const u32 src_extent_x = config.src_x1 - config.src_x0;
const u32 src_extent_y = config.src_y1 - config.src_y0;
@@ -206,8 +205,8 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y,
src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel);
} else {
ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
}
// Conversion Phase
@@ -230,9 +229,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y,
dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel);
} else {
ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
dst.pitch, config.dst_x0, config.dst_y0,
static_cast<size_t>(dst_bytes_per_pixel));
process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
dst.pitch, config.dst_x0, config.dst_y0,
static_cast<size_t>(dst_bytes_per_pixel));
}
return true;
}

View File

@@ -250,6 +250,30 @@ struct GPU::Impl {
gpu_thread.SubmitList(channel, std::move(entries));
}
/// Push GPU command buffer entries to be processed
void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
if (!use_nvdec) {
return;
}
if (!cdma_pushers.contains(id)) {
cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(host1x));
}
// SubmitCommandBuffer would make the nvdec operations async, this is not currently working
// TODO(ameerj): RE proper async nvdec operation
// gpu_thread.SubmitCommandBuffer(std::move(entries));
cdma_pushers[id]->ProcessEntries(std::move(entries));
}
/// Frees the CDMAPusher instance to free up resources
void ClearCdmaInstance(u32 id) {
const auto iter = cdma_pushers.find(id);
if (iter != cdma_pushers.end()) {
cdma_pushers.erase(iter);
}
}
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
gpu_thread.SwapBuffers(framebuffer);
@@ -332,6 +356,7 @@ struct GPU::Impl {
Core::System& system;
Host1x::Host1x& host1x;
std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers;
std::unique_ptr<VideoCore::RendererBase> renderer;
VideoCore::RasterizerInterface* rasterizer = nullptr;
const bool use_nvdec;
@@ -521,6 +546,14 @@ void GPU::PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
impl->PushGPUEntries(channel, std::move(entries));
}
void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
impl->PushCommandBuffer(id, entries);
}
void GPU::ClearCdmaInstance(u32 id) {
impl->ClearCdmaInstance(id);
}
void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
impl->SwapBuffers(framebuffer);
}

View File

@@ -232,6 +232,12 @@ public:
/// Push GPU command entries to be processed
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries);
/// Push GPU command buffer entries to be processed
void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);
/// Frees the CDMAPusher instance to free up resources
void ClearCdmaInstance(u32 id);
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);

View File

@@ -12,7 +12,6 @@
#include "video_core/dma_pusher.h"
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
namespace VideoCommon::GPUThread {

View File

@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/codec.h"
#include "video_core/host1x/codecs/h264.h"
#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra {
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
Codec::~Codec() = default;
void Codec::Initialize() {
initialized = decode_api.Initialize(current_codec);
}
void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
if (current_codec != codec) {
current_codec = codec;
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
}
}
void Codec::Decode() {
const bool is_first_frame = !initialized;
if (is_first_frame) {
Initialize();
}
if (!initialized) {
return;
}
// Assemble bitstream.
bool vp9_hidden_frame = false;
size_t configuration_size = 0;
const auto packet_data = [&]() {
switch (current_codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return vp8_decoder->ComposeFrame(state);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
vp9_decoder->ComposeFrame(state);
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
return vp9_decoder->GetFrameBytes();
default:
ASSERT(false);
return std::span<const u8>{};
}
}();
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data, configuration_size)) {
return;
}
// Only receive/store visible frames.
if (vp9_hidden_frame) {
return;
}
// Receive output frames from decoder.
decode_api.ReceiveFrames(frames);
while (frames.size() > 10) {
LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
frames.pop();
}
}
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
// Sometimes VIC will request more frames than have been decoded.
// in this case, return a blank frame and don't overwrite previous data.
if (frames.empty()) {
return {};
}
auto frame = std::move(frames.front());
frames.pop();
return frame;
}
Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
return current_codec;
}
std::string_view Codec::GetCurrentCodecName() const {
switch (current_codec) {
case Host1x::NvdecCommon::VideoCodec::None:
return "None";
case Host1x::NvdecCommon::VideoCodec::H264:
return "H264";
case Host1x::NvdecCommon::VideoCodec::VP8:
return "VP8";
case Host1x::NvdecCommon::VideoCodec::H265:
return "H265";
case Host1x::NvdecCommon::VideoCodec::VP9:
return "VP9";
default:
return "Unknown";
}
}
} // namespace Tegra

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
namespace Decoder {
class H264;
class VP8;
class VP9;
} // namespace Decoder
namespace Host1x {
class Host1x;
} // namespace Host1x
class Codec {
public:
explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs);
~Codec();
/// Initialize the codec, returning success or failure
void Initialize();
/// Sets NVDEC video stream codec
void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec);
/// Call decoders to construct headers, decode AVFrame with ffmpeg
void Decode();
/// Returns next decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
/// Return name of the current codec
[[nodiscard]] std::string_view GetCurrentCodecName() const;
private:
bool initialized{};
Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& state;
std::unique_ptr<Decoder::H264> h264_decoder;
std::unique_ptr<Decoder::VP8> vp8_decoder;
std::unique_ptr<Decoder::VP9> vp9_decoder;
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
};
} // namespace Tegra

View File

@@ -1,67 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra {
Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_)
: host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_} {}
Decoder::~Decoder() = default;
void Decoder::Decode() {
if (!initialized) {
return;
}
const auto packet_data = ComposeFrame();
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data)) {
return;
}
// Only receive/store visible frames.
if (vp9_hidden_frame) {
return;
}
// Receive output frames from decoder.
auto frame = decode_api.ReceiveFrame();
if (IsInterlaced()) {
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
auto frame_copy = frame;
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode interlaced frame for top 0x{:X} bottom 0x{:X}",
luma_top, luma_bottom);
}
if (UsingDecodeOrder()) {
decode_order_frames.insert_or_assign(luma_top, std::move(frame));
decode_order_frames.insert_or_assign(luma_bottom, std::move(frame_copy));
} else {
presentation_order_frames.push(std::move(frame));
presentation_order_frames.push(std::move(frame_copy));
}
} else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode progressive frame for luma 0x{:X}", luma_offset);
}
if (UsingDecodeOrder()) {
decode_order_frames.insert_or_assign(luma_offset, std::move(frame));
} else {
presentation_order_frames.push(std::move(frame));
}
}
}
} // namespace Tegra

View File

@@ -1,80 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include <unordered_map>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
namespace Host1x {
class Host1x;
} // namespace Host1x
class Decoder {
public:
virtual ~Decoder();
/// Call decoders to construct headers, decode AVFrame with ffmpeg
void Decode();
bool UsingDecodeOrder() const {
return decode_api.UsingDecodeOrder();
}
std::shared_ptr<FFmpeg::Frame> GetFrame(u64 luma_offset) {
if (UsingDecodeOrder()) {
auto it = decode_order_frames.find(luma_offset);
if (it == decode_order_frames.end()) {
return {};
}
return decode_order_frames.extract(it).mapped();
}
if (presentation_order_frames.size() == 0) {
return {};
}
auto frame = std::move(presentation_order_frames.front());
presentation_order_frames.pop();
return frame;
}
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const {
return codec;
}
/// Return name of the current codec
[[nodiscard]] virtual std::string_view GetCurrentCodecName() const = 0;
protected:
explicit Decoder(Host1x::Host1x& host1x, s32 id,
const Host1x::NvdecCommon::NvdecRegisters& regs);
virtual std::span<const u8> ComposeFrame() = 0;
virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0;
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0;
Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id;
Host1x::NvdecCommon::VideoCodec codec;
FFmpeg::DecodeApi decode_api;
bool initialized{};
std::queue<std::shared_ptr<FFmpeg::Frame>> presentation_order_frames;
std::unordered_map<u64, std::shared_ptr<FFmpeg::Frame>> decode_order_frames;
bool vp9_hidden_frame{};
};
} // namespace Tegra

View File

@@ -10,7 +10,7 @@
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra::Decoders {
namespace Tegra::Decoder {
namespace {
// ZigZag LUTs from libavcodec.
constexpr std::array<u8, 64> zig_zag_direct{
@@ -25,55 +25,23 @@ constexpr std::array<u8, 16> zig_zag_scan{
};
} // Anonymous namespace
H264::H264(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
: Decoder{host1x_, id_, regs_} {
codec = Host1x::NvdecCommon::VideoCodec::H264;
initialized = decode_api.Initialize(codec);
}
H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
H264::~H264() = default;
std::tuple<u64, u64> H264::GetProgressiveOffsets() {
auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
auto luma{regs.surface_luma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.luma_frame_offset.Address()};
auto chroma{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_frame_offset.Address()};
return {luma, chroma};
}
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
size_t* out_configuration_size, bool is_first_frame) {
H264DecoderContext context;
host1x.GMMU().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
std::tuple<u64, u64, u64, u64> H264::GetInterlacedOffsets() {
auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
auto luma_top{regs.surface_luma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.luma_top_offset.Address()};
auto luma_bottom{regs.surface_luma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.luma_bot_offset.Address()};
auto chroma_top{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_top_offset.Address()};
auto chroma_bottom{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_bot_offset.Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
bool H264::IsInterlaced() {
return current_context.h264_parameter_set.luma_top_offset.Address() != 0 ||
current_context.h264_parameter_set.luma_bot_offset.Address() != 0;
}
std::span<const u8> H264::ComposeFrame() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(H264DecoderContext));
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
const s64 frame_number = context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len);
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(),
frame_scratch.size());
return frame_scratch;
frame.resize_destructive(context.stream_len);
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
*out_configuration_size = 0;
return frame;
}
is_first_frame = false;
// Encode header
H264BitWriter writer{};
writer.WriteU(1, 24);
@@ -85,7 +53,7 @@ std::span<const u8> H264::ComposeFrame() {
writer.WriteU(31, 8);
writer.WriteUe(0);
const u32 chroma_format_idc =
static_cast<u32>(current_context.h264_parameter_set.chroma_format_idc.Value());
static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
writer.WriteUe(chroma_format_idc);
if (chroma_format_idc == 3) {
writer.WriteBit(false);
@@ -93,44 +61,42 @@ std::span<const u8> H264::ComposeFrame() {
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(current_context.qpprime_y_zero_transform_bypass_flag.Value() != 0);
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
writer.WriteBit(false); // Scaling matrix present flag
writer.WriteUe(
static_cast<u32>(current_context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
const auto order_cnt_type =
static_cast<u32>(current_context.h264_parameter_set.pic_order_cnt_type.Value());
static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value());
writer.WriteUe(order_cnt_type);
if (order_cnt_type == 0) {
writer.WriteUe(current_context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
} else if (order_cnt_type == 1) {
writer.WriteBit(current_context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteSe(0);
writer.WriteSe(0);
writer.WriteUe(0);
}
const s32 pic_height = current_context.h264_parameter_set.frame_height_in_mbs /
(current_context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
u32 max_num_ref_frames =
std::max(std::max(current_context.h264_parameter_set.num_refidx_l0_default_active,
current_context.h264_parameter_set.num_refidx_l1_default_active) +
1,
4);
// TODO (ameerj): Where do we get this number, it seems to be particular for each stream
const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue();
const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::Gpu;
const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u;
writer.WriteUe(max_num_ref_frames);
writer.WriteBit(false);
writer.WriteUe(current_context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1);
writer.WriteBit(current_context.h264_parameter_set.frame_mbs_only_flag != 0);
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
if (!current_context.h264_parameter_set.frame_mbs_only_flag) {
writer.WriteBit(current_context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
if (!context.h264_parameter_set.frame_mbs_only_flag) {
writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
}
writer.WriteBit(current_context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
writer.WriteBit(false); // Frame cropping flag
writer.WriteBit(false); // VUI parameter present flag
@@ -145,59 +111,57 @@ std::span<const u8> H264::ComposeFrame() {
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(current_context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(current_context.h264_parameter_set.pic_order_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(context.h264_parameter_set.pic_order_present_flag != 0);
writer.WriteUe(0);
writer.WriteUe(current_context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(current_context.h264_parameter_set.num_refidx_l1_default_active);
writer.WriteBit(current_context.h264_parameter_set.flags.weighted_pred.Value() != 0);
writer.WriteU(static_cast<s32>(current_context.h264_parameter_set.weighted_bipred_idc.Value()),
2);
s32 pic_init_qp =
static_cast<s32>(current_context.h264_parameter_set.pic_init_qp_minus26.Value());
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0);
writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2);
s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value());
writer.WriteSe(pic_init_qp);
writer.WriteSe(0);
s32 chroma_qp_index_offset =
static_cast<s32>(current_context.h264_parameter_set.chroma_qp_index_offset.Value());
static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset);
writer.WriteBit(current_context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
writer.WriteBit(current_context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
writer.WriteBit(current_context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
writer.WriteBit(current_context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true); // pic_scaling_matrix_present_flag
for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true);
std::span<const u8> matrix{current_context.weight_scale_4x4};
writer.WriteScalingList(scan_scratch, matrix, index * 16, 16);
std::span<const u8> matrix{context.weight_scale};
writer.WriteScalingList(scan, matrix, index * 16, 16);
}
if (current_context.h264_parameter_set.transform_8x8_mode_flag) {
if (context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true);
std::span<const u8> matrix{current_context.weight_scale_8x8};
writer.WriteScalingList(scan_scratch, matrix, index * 64, 64);
std::span<const u8> matrix{context.weight_scale_8x8};
writer.WriteScalingList(scan, matrix, index * 64, 64);
}
}
s32 chroma_qp_index_offset2 =
static_cast<s32>(current_context.h264_parameter_set.second_chroma_qp_index_offset.Value());
static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset2);
writer.End();
const auto& encoded_header = writer.GetByteArray();
frame_scratch.resize(encoded_header.size() + current_context.stream_len);
std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size());
frame.resize(encoded_header.size() + context.stream_len);
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
frame_scratch.data() + encoded_header.size(),
current_context.stream_len);
*out_configuration_size = encoded_header.size();
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(),
context.stream_len);
return frame_scratch;
return frame;
}
H264BitWriter::H264BitWriter() = default;
@@ -314,4 +278,4 @@ void H264BitWriter::Flush() {
buffer = 0;
buffer_pos = 0;
}
} // namespace Tegra::Decoders
} // namespace Tegra::Decoder

View File

@@ -10,7 +10,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
@@ -19,7 +18,7 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
namespace Decoders {
namespace Decoder {
class H264BitWriter {
public:
@@ -61,212 +60,123 @@ private:
std::vector<u8> byte_array;
};
struct Offset {
constexpr u32 Address() const noexcept {
return offset << 8;
}
class H264 {
public:
explicit H264(Host1x::Host1x& host1x);
~H264();
/// Compose the H264 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
size_t* out_configuration_size,
bool is_first_frame = false);
private:
u32 offset;
};
static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
static_assert(sizeof(Offset) == 0x4, "Offset has the wrong size!");
Common::ScratchBuffer<u8> frame;
Common::ScratchBuffer<u8> scan;
Host1x::Host1x& host1x;
struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_mbs; ///< 0x10
union { ///< 0x14
BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height;
BitField<5, 27, u32> reserved_surface_format;
struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_map_units; ///< 0x10
union { ///< 0x14
BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height;
};
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
u32 luma_top_offset; ///< 0x3C
u32 luma_bot_offset; ///< 0x40
u32 luma_frame_offset; ///< 0x44
u32 chroma_top_offset; ///< 0x48
u32 chroma_bot_offset; ///< 0x4C
u32 chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
};
};
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
Offset luma_top_offset; ///< 0x3C
Offset luma_bot_offset; ///< 0x40
Offset luma_frame_offset; ///< 0x44
Offset chroma_top_offset; ///< 0x48
Offset chroma_bot_offset; ///< 0x4C
Offset chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000
u32 stream_len; ///< 0x0048
INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C
H264ParameterSet h264_parameter_set; ///< 0x0058
INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8
std::array<u8, 0x60> weight_scale; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
};
};
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_mbs, 0x10);
ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58);
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_map_units, 0x10);
ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION
struct DpbEntry {
union {
BitField<0, 7, u32> index;
BitField<7, 5, u32> col_idx;
BitField<12, 2, u32> state;
BitField<14, 1, u32> is_long_term;
BitField<15, 1, u32> non_existing;
BitField<16, 1, u32> is_field;
BitField<17, 4, u32> top_field_marking;
BitField<21, 4, u32> bottom_field_marking;
BitField<25, 1, u32> output_memory_layout;
BitField<26, 6, u32> reserved;
} flags;
std::array<u32, 2> field_order_cnt;
u32 frame_idx;
};
static_assert(sizeof(DpbEntry) == 0x10, "DpbEntry has the wrong size!");
struct DisplayParam {
union {
BitField<0, 1, u32> enable_tf_output;
BitField<1, 1, u32> vc1_map_y_flag;
BitField<2, 3, u32> map_y_value;
BitField<5, 1, u32> vc1_map_uv_flag;
BitField<6, 3, u32> map_uv_value;
BitField<9, 8, u32> out_stride;
BitField<17, 3, u32> tiling_format;
BitField<20, 1, u32> output_structure; // 0=frame, 1=field
BitField<21, 11, u32> reserved0;
};
std::array<s32, 2> output_top;
std::array<s32, 2> output_bottom;
union {
BitField<0, 1, u32> enable_histogram;
BitField<1, 12, u32> histogram_start_x;
BitField<13, 12, u32> histogram_start_y;
BitField<25, 7, u32> reserved1;
};
union {
BitField<0, 12, u32> histogram_end_x;
BitField<12, 12, u32> histogram_end_y;
BitField<24, 8, u32> reserved2;
};
};
static_assert(sizeof(DisplayParam) == 0x1C, "DisplayParam has the wrong size!");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(13); ///< 0x0000
std::array<u8, 16> eos; ///< 0x0034
u8 explicit_eos_present_flag; ///< 0x0044
u8 hint_dump_en; ///< 0x0045
INSERT_PADDING_BYTES_NOINIT(2); ///< 0x0046
u32 stream_len; ///< 0x0048
u32 slice_count; ///< 0x004C
u32 mbhist_buffer_size; ///< 0x0050
u32 gptimer_timeout_value; ///< 0x0054
H264ParameterSet h264_parameter_set; ///< 0x0058
std::array<s32, 2> curr_field_order_cnt; ///< 0x00B8
std::array<DpbEntry, 16> dpb; ///< 0x00C0
std::array<u8, 0x60> weight_scale_4x4; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
std::array<u8, 2> num_inter_view_refs_lX; ///< 0x02A0
std::array<u8, 14> reserved2; ///< 0x02A2
std::array<std::array<s8, 16>, 2> inter_view_refidx_lX; ///< 0x02B0
union { ///< 0x02D0
BitField<0, 1, u32> lossless_ipred8x8_filter_enable;
BitField<1, 1, u32> qpprime_y_zero_transform_bypass_flag;
BitField<2, 30, u32> reserved3;
};
DisplayParam display_param; ///< 0x02D4
std::array<u32, 3> reserved4; ///< 0x02F0
};
static_assert(sizeof(H264DecoderContext) == 0x2FC, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(dpb, 0xC0);
ASSERT_POSITION(weight_scale_4x4, 0x1C0);
ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(weight_scale, 0x1C0);
#undef ASSERT_POSITION
class H264 final : public Decoder {
public:
explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
~H264() override;
H264(const H264&) = delete;
H264& operator=(const H264&) = delete;
H264(H264&&) = delete;
H264& operator=(H264&&) = delete;
/// Compose the H264 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame() override;
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override;
std::string_view GetCurrentCodecName() const override {
return "H264";
}
private:
bool is_first_frame{true};
Common::ScratchBuffer<u8> frame_scratch;
Common::ScratchBuffer<u8> scan_scratch;
H264DecoderContext current_context{};
};
} // namespace Decoders
} // namespace Decoder
} // namespace Tegra

View File

@@ -7,69 +7,47 @@
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra::Decoders {
VP8::VP8(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
: Decoder{host1x_, id_, regs_} {
codec = Host1x::NvdecCommon::VideoCodec::VP8;
initialized = decode_api.Initialize(codec);
}
namespace Tegra::Decoder {
VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {}
VP8::~VP8() = default;
std::tuple<u64, u64> VP8::GetProgressiveOffsets() {
auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
return {luma, chroma};
}
std::span<const u8> VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
VP8PictureInfo info;
host1x.GMMU().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo));
std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto luma_bottom{
regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_top{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_bottom{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
std::span<const u8> VP8::ComposeFrame() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(VP8PictureInfo));
const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = static_cast<size_t>(current_context.vld_buffer_size);
const bool is_key_frame = info.key_frame == 1u;
const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size);
const size_t header_size = is_key_frame ? 10u : 3u;
frame_scratch.resize(header_size + bitstream_size);
frame.resize(header_size + bitstream_size);
// Based on page 30 of the VP8 specification.
// https://datatracker.ietf.org/doc/rfc6386/
frame_scratch[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
frame_scratch[0] |=
static_cast<u8>((current_context.version & 7u) << 1u); // 3-bit version number
frame_scratch[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
frame[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
frame[0] |= static_cast<u8>((info.version & 7u) << 1u); // 3-bit version number
frame[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
// The next 19-bits are the first partition size
frame_scratch[0] |= static_cast<u8>((current_context.first_part_size & 7u) << 5u);
frame_scratch[1] = static_cast<u8>((current_context.first_part_size & 0x7f8u) >> 3u);
frame_scratch[2] = static_cast<u8>((current_context.first_part_size & 0x7f800u) >> 11u);
frame[0] |= static_cast<u8>((info.first_part_size & 7u) << 5u);
frame[1] = static_cast<u8>((info.first_part_size & 0x7f8u) >> 3u);
frame[2] = static_cast<u8>((info.first_part_size & 0x7f800u) >> 11u);
if (is_key_frame) {
frame_scratch[3] = 0x9du;
frame_scratch[4] = 0x01u;
frame_scratch[5] = 0x2au;
frame[3] = 0x9du;
frame[4] = 0x01u;
frame[5] = 0x2au;
// TODO(ameerj): Horizontal/Vertical Scale
// 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits)
frame_scratch[6] = static_cast<u8>(current_context.frame_width & 0xff);
frame_scratch[7] = static_cast<u8>(((current_context.frame_width >> 8) & 0x3f));
frame[6] = static_cast<u8>(info.frame_width & 0xff);
frame[7] = static_cast<u8>(((info.frame_width >> 8) & 0x3f));
// 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits)
frame_scratch[8] = static_cast<u8>(current_context.frame_height & 0xff);
frame_scratch[9] = static_cast<u8>(((current_context.frame_height >> 8) & 0x3f));
frame[8] = static_cast<u8>(info.frame_height & 0xff);
frame[9] = static_cast<u8>(((info.frame_height >> 8) & 0x3f));
}
const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
memory_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
const u64 bitstream_offset = state.frame_bitstream_offset;
host1x.GMMU().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size);
return frame_scratch;
return frame;
}
} // namespace Tegra::Decoders
} // namespace Tegra::Decoder

View File

@@ -9,7 +9,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
@@ -18,40 +17,20 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
namespace Decoders {
enum class Vp8SurfaceIndex : u32 {
Last = 0,
Golden = 1,
AltRef = 2,
Current = 3,
};
namespace Decoder {
class VP8 final : public Decoder {
class VP8 {
public:
explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
~VP8() override;
explicit VP8(Host1x::Host1x& host1x);
~VP8();
VP8(const VP8&) = delete;
VP8& operator=(const VP8&) = delete;
VP8(VP8&&) = delete;
VP8& operator=(VP8&&) = delete;
[[nodiscard]] std::span<const u8> ComposeFrame() override;
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override {
return false;
}
std::string_view GetCurrentCodecName() const override {
return "VP8";
}
/// Compose the VP8 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame(
const Host1x::NvdecCommon::NvdecRegisters& state);
private:
Common::ScratchBuffer<u8> frame_scratch;
Common::ScratchBuffer<u8> frame;
Host1x::Host1x& host1x;
struct VP8PictureInfo {
INSERT_PADDING_WORDS_NOINIT(14);
@@ -94,9 +73,7 @@ private:
INSERT_PADDING_WORDS_NOINIT(3);
};
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
VP8PictureInfo current_context{};
};
} // namespace Decoders
} // namespace Decoder
} // namespace Tegra

View File

@@ -4,13 +4,12 @@
#include <algorithm> // for std::copy
#include <numeric>
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra::Decoders {
namespace Tegra::Decoder {
namespace {
constexpr u32 diff_update_probability = 252;
constexpr u32 frame_sync_code = 0x498342;
@@ -238,11 +237,7 @@ constexpr std::array<u8, 254> map_lut{
}
} // Anonymous namespace
VP9::VP9(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
: Decoder{host1x_, id_, regs_} {
codec = Host1x::NvdecCommon::VideoCodec::VP9;
initialized = decode_api.Initialize(codec);
}
VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {}
VP9::~VP9() = default;
@@ -361,113 +356,35 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
}
}
void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
bool enabled = current_picture_info.segmentation.enabled != 0;
writer.WriteBit(enabled);
if (!enabled) {
return;
}
Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) {
PictureInfo picture_info;
host1x.GMMU().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
Vp9PictureInfo vp9_info = picture_info.Convert();
auto update_map = current_picture_info.segmentation.update_map != 0;
writer.WriteBit(update_map);
if (update_map) {
EntropyProbs entropy_probs{};
memory_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs,
sizeof(entropy_probs));
auto WriteProb = [&](u8 prob) {
bool coded = prob != 255;
writer.WriteBit(coded);
if (coded) {
writer.WriteU(prob, 8);
}
};
for (size_t i = 0; i < entropy_probs.mb_segment_tree_probs.size(); i++) {
WriteProb(entropy_probs.mb_segment_tree_probs[i]);
}
auto temporal_update = current_picture_info.segmentation.temporal_update != 0;
writer.WriteBit(temporal_update);
if (temporal_update) {
for (s32 i = 0; i < 3; i++) {
WriteProb(entropy_probs.segment_pred_probs[i]);
}
}
}
if (last_segmentation == current_picture_info.segmentation) {
writer.WriteBit(false);
return;
}
last_segmentation = current_picture_info.segmentation;
writer.WriteBit(true);
writer.WriteBit(current_picture_info.segmentation.abs_delta != 0);
constexpr s32 MAX_SEGMENTS = 8;
constexpr std::array SegmentationFeatureBits = {8, 6, 2, 0};
for (s32 i = 0; i < MAX_SEGMENTS; i++) {
auto q_enabled = current_picture_info.segmentation.feature_enabled[i][0] != 0;
writer.WriteBit(q_enabled);
if (q_enabled) {
writer.WriteS(current_picture_info.segmentation.feature_data[i][0],
SegmentationFeatureBits[0]);
}
auto lf_enabled = current_picture_info.segmentation.feature_enabled[i][1] != 0;
writer.WriteBit(lf_enabled);
if (lf_enabled) {
writer.WriteS(current_picture_info.segmentation.feature_data[i][1],
SegmentationFeatureBits[1]);
}
auto ref_enabled = current_picture_info.segmentation.feature_enabled[i][2] != 0;
writer.WriteBit(ref_enabled);
if (ref_enabled) {
writer.WriteU(current_picture_info.segmentation.feature_data[i][2],
SegmentationFeatureBits[2]);
}
auto skip_enabled = current_picture_info.segmentation.feature_enabled[i][3] != 0;
writer.WriteBit(skip_enabled);
}
}
Vp9PictureInfo VP9::GetVp9PictureInfo() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_picture_info,
sizeof(PictureInfo));
Vp9PictureInfo vp9_info = current_picture_info.Convert();
InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy);
InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
// surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
// order: last, golden, altref, current.
for (size_t i = 0; i < 4; i++) {
vp9_info.frame_offsets[i] = regs.surface_luma_offsets[i].Address();
}
std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4,
vp9_info.frame_offsets.begin());
return vp9_info;
}
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
EntropyProbs entropy;
memory_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
host1x.GMMU().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
entropy.Convert(dst);
}
Vp9FrameContainer VP9::GetCurrentFrame() {
Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
Vp9FrameContainer current_frame{};
{
// gpu.SyncGuestHost(); epic, why?
current_frame.info = GetVp9PictureInfo();
current_frame.info = GetVp9PictureInfo(state);
current_frame.bit_stream.resize(current_frame.info.bitstream_size);
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
current_frame.bit_stream.data(),
current_frame.info.bitstream_size);
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(),
current_frame.info.bitstream_size);
}
if (!next_frame.bit_stream.empty()) {
Vp9FrameContainer temp{
@@ -825,7 +742,8 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
WriteSegmentation(uncomp_writer);
ASSERT(!current_frame_info.segment_enabled);
uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width);
@@ -852,29 +770,10 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
return uncomp_writer;
}
std::tuple<u64, u64> VP9::GetProgressiveOffsets() {
auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
return {luma, chroma};
}
std::tuple<u64, u64, u64, u64> VP9::GetInterlacedOffsets() {
auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto luma_bottom{
regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma_top{
regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma_bottom{
regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
std::span<const u8> VP9::ComposeFrame() {
vp9_hidden_frame = false;
void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
std::vector<u8> bitstream;
{
Vp9FrameContainer curr_frame = GetCurrentFrame();
Vp9FrameContainer curr_frame = GetCurrentFrame(state);
current_frame_info = curr_frame.info;
bitstream = std::move(curr_frame.bit_stream);
}
@@ -887,16 +786,12 @@ std::span<const u8> VP9::ComposeFrame() {
std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray();
// Write headers and frame to buffer
frame_scratch.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame_scratch.begin());
frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame.begin());
std::copy(compressed_header.begin(), compressed_header.end(),
frame_scratch.begin() + uncompressed_header.size());
frame.begin() + uncompressed_header.size());
std::copy(bitstream.begin(), bitstream.end(),
frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
vp9_hidden_frame = WasFrameHidden();
return GetFrameBytes();
frame.begin() + uncompressed_header.size() + compressed_header.size());
}
VpxRangeEncoder::VpxRangeEncoder() {
@@ -1049,4 +944,4 @@ const std::vector<u8>& VpxBitStreamWriter::GetByteArray() const {
return byte_array;
}
} // namespace Tegra::Decoders
} // namespace Tegra::Decoder

View File

@@ -10,7 +10,6 @@
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/stream.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codecs/vp9_types.h"
#include "video_core/host1x/nvdec_common.h"
@@ -20,7 +19,7 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
namespace Decoders {
namespace Decoder {
/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
/// VP9 header bitstreams.
@@ -111,31 +110,21 @@ private:
std::vector<u8> byte_array;
};
class VP9 final : public Decoder {
class VP9 {
public:
explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
~VP9() override;
explicit VP9(Host1x::Host1x& host1x);
~VP9();
VP9(const VP9&) = delete;
VP9& operator=(const VP9&) = delete;
VP9(VP9&&) = delete;
VP9(VP9&&) = default;
VP9& operator=(VP9&&) = delete;
[[nodiscard]] std::span<const u8> ComposeFrame() override;
/// Composes the VP9 frame from the GPU state information.
/// Based on the official VP9 spec documentation
void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state);
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override {
return false;
}
std::string_view GetCurrentCodecName() const override {
return "VP9";
}
private:
/// Returns true if the most recent frame was a hidden frame.
[[nodiscard]] bool WasFrameHidden() const {
return !current_frame_info.show_frame;
@@ -143,9 +132,10 @@ private:
/// Returns a const span to the composed frame data.
[[nodiscard]] std::span<const u8> GetFrameBytes() const {
return frame_scratch;
return frame;
}
private:
/// Generates compressed header probability updates in the bitstream writer
template <typename T, std::size_t N>
void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
@@ -177,22 +167,23 @@ private:
/// Write motion vector probability updates. 6.3.17 in the spec
void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
void WriteSegmentation(VpxBitStreamWriter& writer);
/// Returns VP9 information from NVDEC provided offset and size
[[nodiscard]] Vp9PictureInfo GetVp9PictureInfo();
[[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(
const Host1x::NvdecCommon::NvdecRegisters& state);
/// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
/// Returns frame to be decoded after buffering
[[nodiscard]] Vp9FrameContainer GetCurrentFrame();
[[nodiscard]] Vp9FrameContainer GetCurrentFrame(
const Host1x::NvdecCommon::NvdecRegisters& state);
/// Use NVDEC providied information to compose the headers for the current frame
[[nodiscard]] std::vector<u8> ComposeCompressedHeader();
[[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
Common::ScratchBuffer<u8> frame_scratch;
Host1x::Host1x& host1x;
Common::ScratchBuffer<u8> frame;
std::array<s8, 4> loop_filter_ref_deltas{};
std::array<s8, 2> loop_filter_mode_deltas{};
@@ -201,11 +192,9 @@ private:
std::array<Vp9EntropyProbs, 4> frame_ctxs{};
bool swap_ref_indices{};
Segmentation last_segmentation{};
PictureInfo current_picture_info{};
Vp9PictureInfo current_frame_info{};
Vp9EntropyProbs prev_frame_probs{};
};
} // namespace Decoders
} // namespace Decoder
} // namespace Tegra

View File

@@ -11,14 +11,7 @@
namespace Tegra {
namespace Decoders {
enum class Vp9SurfaceIndex : u32 {
Last = 0,
Golden = 1,
AltRef = 2,
Current = 3,
};
namespace Decoder {
struct Vp9FrameDimensions {
s16 width;
s16 height;
@@ -55,13 +48,11 @@ enum class TxMode {
};
struct Segmentation {
constexpr bool operator==(const Segmentation& rhs) const = default;
u8 enabled;
u8 update_map;
u8 temporal_update;
u8 abs_delta;
std::array<std::array<u8, 4>, 8> feature_enabled;
std::array<u32, 8> feature_mask;
std::array<std::array<s16, 4>, 8> feature_data;
};
static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
@@ -199,17 +190,7 @@ struct PictureInfo {
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
struct EntropyProbs {
std::array<u8, 10 * 10 * 8> kf_bmode_prob; ///< 0x0000
std::array<u8, 10 * 10 * 1> kf_bmode_probB; ///< 0x0320
std::array<u8, 3> ref_pred_probs; ///< 0x0384
std::array<u8, 7> mb_segment_tree_probs; ///< 0x0387
std::array<u8, 3> segment_pred_probs; ///< 0x038E
std::array<u8, 4> ref_scores; ///< 0x0391
std::array<u8, 2> prob_comppred; ///< 0x0395
INSERT_PADDING_BYTES_NOINIT(9); ///< 0x0397
std::array<u8, 10 * 8> kf_uv_mode_prob; ///< 0x03A0
std::array<u8, 10 * 1> kf_uv_mode_probB; ///< 0x03F0
INSERT_PADDING_BYTES_NOINIT(6); ///< 0x03FA
INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000
std::array<u8, 28> inter_mode_prob; ///< 0x0400
std::array<u8, 4> intra_inter_prob; ///< 0x041C
INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
@@ -321,5 +302,5 @@ ASSERT_POSITION(class_0_fr, 0x560);
ASSERT_POSITION(coef_probs, 0x5A0);
#undef ASSERT_POSITION
}; // namespace Decoders
}; // namespace Decoder
}; // namespace Tegra

View File

@@ -27,7 +27,6 @@ void Control::ProcessMethod(Method method, u32 argument) {
}
void Control::Execute(u32 data) {
LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value);
host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
}

View File

@@ -6,7 +6,9 @@
#include "common/common_types.h"
namespace Tegra::Host1x {
namespace Tegra {
namespace Host1x {
class Host1x;
class Nvdec;
@@ -29,8 +31,10 @@ private:
/// For Host1x, execute is waiting on a syncpoint previously written into the state
void Execute(u32 data);
Host1x& host1x;
u32 syncpoint_value{};
Host1x& host1x;
};
} // namespace Tegra::Host1x
} // namespace Host1x
} // namespace Tegra

View File

@@ -5,9 +5,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/memory_manager.h"
extern "C" {
#ifdef LIBVA_FOUND
@@ -185,8 +183,8 @@ bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
return true;
}
DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
DecoderContext::DecoderContext(const Decoder& decoder) {
m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
@@ -218,25 +216,6 @@ bool DecoderContext::OpenContext(const Decoder& decoder) {
}
bool DecoderContext::SendPacket(const Packet& packet) {
m_temp_frame = std::make_shared<Frame>();
m_got_frame = 0;
// Android can randomly crash when calling decode directly, so skip.
// TODO update ffmpeg and hope that fixes it.
#ifndef ANDROID
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
m_decode_order = true;
auto* codec{ffcodec(m_decoder.GetCodec())};
if (const int ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(),
&m_got_frame, packet.GetPacket());
ret < 0) {
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", AVError(ret));
return false;
}
return true;
}
#endif
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
return false;
@@ -245,73 +224,139 @@ bool DecoderContext::SendPacket(const Packet& packet) {
return true;
}
std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
// Android can randomly crash when calling decode directly, so skip.
// TODO update ffmpeg and hope that fixes it.
#ifndef ANDROID
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
m_decode_order = true;
auto* codec{ffcodec(m_decoder.GetCodec())};
int ret{0};
std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
auto dst_frame = std::make_unique<Frame>();
if (m_got_frame == 0) {
Packet packet{{}};
auto* pkt = packet.GetPacket();
pkt->data = nullptr;
pkt->size = 0;
ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(), &m_got_frame, pkt);
m_codec_context->has_b_frames = 0;
const auto ReceiveImpl = [&](AVFrame* frame) {
if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
return false;
}
if (m_got_frame == 0 || ret < 0) {
LOG_ERROR(Service_NVDRV, "Failed to receive a frame! error {}", ret);
*out_is_interlaced =
#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
(frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
#else
frame->interlaced_frame != 0;
#endif
return true;
};
if (m_codec_context->hw_device_ctx) {
// If we have a hardware context, make a separate frame here to receive the
// hardware result before sending it to the output.
Frame intermediate_frame;
if (!ReceiveImpl(intermediate_frame.GetFrame())) {
return {};
}
} else
#endif
{
const auto ReceiveImpl = [&](AVFrame* frame) {
if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
return false;
}
return true;
};
if (m_codec_context->hw_device_ctx) {
// If we have a hardware context, make a separate frame here to receive the
// hardware result before sending it to the output.
Frame intermediate_frame;
if (!ReceiveImpl(intermediate_frame.GetFrame())) {
return {};
}
m_temp_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(),
intermediate_frame.GetFrame(), 0);
ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
} else {
// Otherwise, decode the frame as normal.
if (!ReceiveImpl(m_temp_frame->GetFrame())) {
return {};
}
dst_frame->SetFormat(PreferredGpuFormat);
if (const int ret =
av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
} else {
// Otherwise, decode the frame as normal.
if (!ReceiveImpl(dst_frame->GetFrame())) {
return {};
}
}
#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
m_temp_frame->GetFrame()->interlaced_frame =
(m_temp_frame->GetFrame()->flags & AV_FRAME_FLAG_INTERLACED) != 0;
#endif
return std::move(m_temp_frame);
return dst_frame;
}
DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
const AVFilter* buffer_src = avfilter_get_by_name("buffer");
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
SCOPE_EXIT({
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
});
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
// so just use 1/1 to make buffer filter happy
std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
m_filter_graph = avfilter_graph_alloc();
int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
nullptr, m_filter_graph);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
return;
}
ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
m_filter_graph);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
return;
}
inputs->name = av_strdup("out");
inputs->filter_ctx = m_sink_context;
inputs->pad_idx = 0;
inputs->next = nullptr;
outputs->name = av_strdup("in");
outputs->filter_ctx = m_source_context;
outputs->pad_idx = 0;
outputs->next = nullptr;
const char* description = "yadif=1:-1:0";
ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
return;
}
ret = avfilter_graph_config(m_filter_graph, nullptr);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
return;
}
m_initialized = true;
}
bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
AV_BUFFERSRC_FLAG_KEEP_REF);
ret < 0) {
LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
return false;
}
return true;
}
std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
auto dst_frame = std::make_unique<Frame>();
const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
return {};
}
if (ret < 0) {
LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
return {};
}
return dst_frame;
}
DeinterlaceFilter::~DeinterlaceFilter() {
avfilter_graph_free(&m_filter_graph);
}
void DecodeApi::Reset() {
m_deinterlace_filter.reset();
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
@@ -337,14 +382,43 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
return true;
}
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
FFmpeg::Packet packet(packet_data);
return m_decoder_context->SendPacket(packet);
}
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
// Receive raw frame from decoder.
return m_decoder_context->ReceiveFrame();
bool is_interlaced;
auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
if (!frame) {
return;
}
if (!is_interlaced) {
// If the frame is not interlaced, we can pend it now.
frame_queue.push(std::move(frame));
} else {
// Create the deinterlacer if needed.
if (!m_deinterlace_filter) {
m_deinterlace_filter.emplace(*frame);
}
// Add the frame we just received.
if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
return;
}
// Pend output fields.
while (true) {
auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
if (!filter_frame) {
break;
}
frame_queue.push(std::move(filter_frame));
}
}
}
} // namespace FFmpeg

View File

@@ -20,20 +20,17 @@ extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#ifndef ANDROID
#include <libavcodec/codec_internal.h>
#endif
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
namespace Tegra {
class MemoryManager;
}
namespace FFmpeg {
class Packet;
@@ -93,10 +90,6 @@ public:
return m_frame->data[plane];
}
const u8* GetPlane(int plane) const {
return m_frame->data[plane];
}
u8** GetPlanes() const {
return m_frame->data;
}
@@ -105,14 +98,6 @@ public:
m_frame->format = format;
}
bool IsInterlaced() const {
return m_frame->interlaced_frame != 0;
}
bool IsHardwareDecoded() const {
return m_frame->hw_frames_ctx != nullptr;
}
AVFrame* GetFrame() const {
return m_frame;
}
@@ -175,22 +160,33 @@ public:
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
bool OpenContext(const Decoder& decoder);
bool SendPacket(const Packet& packet);
std::shared_ptr<Frame> ReceiveFrame();
std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
AVCodecContext* GetCodecContext() const {
return m_codec_context;
}
bool UsingDecodeOrder() const {
return m_decode_order;
}
private:
AVCodecContext* m_codec_context{};
};
// Wraps an AVFilterGraph.
class DeinterlaceFilter {
public:
YUZU_NON_COPYABLE(DeinterlaceFilter);
YUZU_NON_MOVEABLE(DeinterlaceFilter);
explicit DeinterlaceFilter(const Frame& frame);
~DeinterlaceFilter();
bool AddSourceFrame(const Frame& frame);
std::unique_ptr<Frame> DrainSinkFrame();
private:
const Decoder& m_decoder;
AVCodecContext* m_codec_context{};
s32 m_got_frame{};
std::shared_ptr<Frame> m_temp_frame{};
bool m_decode_order{};
AVFilterGraph* m_filter_graph{};
AVFilterContext* m_source_context{};
AVFilterContext* m_sink_context{};
bool m_initialized{};
};
class DecodeApi {
@@ -204,17 +200,14 @@ public:
bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
void Reset();
bool UsingDecodeOrder() const {
return m_decoder_context->UsingDecodeOrder();
}
bool SendPacket(std::span<const u8> packet_data);
std::shared_ptr<Frame> ReceiveFrame();
bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
private:
std::optional<FFmpeg::Decoder> m_decoder;
std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context;
std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
};
} // namespace FFmpeg

View File

@@ -3,10 +3,10 @@
#include "core/core.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/nvdec.h"
#include "video_core/host1x/vic.h"
namespace Tegra::Host1x {
namespace Tegra {
namespace Host1x {
Host1x::Host1x(Core::System& system_)
: system{system_}, syncpoint_manager{},
@@ -15,23 +15,6 @@ Host1x::Host1x(Core::System& system_)
Host1x::~Host1x() = default;
void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
switch (type) {
case ChannelType::NvDec:
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
last_nvdec_fd = fd;
break;
case ChannelType::VIC:
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, GetLastNvdecDevice(), syncpt);
break;
default:
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type));
break;
}
}
} // namespace Host1x
void Host1x::StopDevice(s32 fd, ChannelType type) {
devices.erase(fd);
}
} // namespace Tegra::Host1x
} // namespace Tegra

View File

@@ -6,7 +6,6 @@
#include "common/common_types.h"
#include "common/address_space.h"
#include "video_core/cdma_pusher.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/host1x/syncpoint_manager.h"
#include "video_core/memory_manager.h"
@@ -15,29 +14,15 @@ namespace Core {
class System;
} // namespace Core
namespace Tegra::Host1x {
class Nvdec;
namespace Tegra {
enum class ChannelType : u32 {
MsEnc = 0,
VIC = 1,
GPU = 2,
NvDec = 3,
Display = 4,
NvJpg = 5,
TSec = 6,
Max = 7,
};
namespace Host1x {
class Host1x {
public:
explicit Host1x(Core::System& system);
~Host1x();
Core::System& System() {
return system;
}
SyncpointManager& GetSyncpointManager() {
return syncpoint_manager;
}
@@ -70,31 +55,14 @@ public:
return *allocator;
}
void StartDevice(s32 fd, ChannelType type, u32 syncpt);
void StopDevice(s32 fd, ChannelType type);
void PushEntries(s32 fd, ChCommandHeaderList&& entries) {
auto it = devices.find(fd);
if (it == devices.end()) {
return;
}
it->second->PushEntries(std::move(entries));
}
Nvdec& GetLastNvdecDevice() {
auto it = devices.find(last_nvdec_fd);
ASSERT(it->second.get() != nullptr);
return *reinterpret_cast<Nvdec*>(it->second.get());
}
private:
Core::System& system;
SyncpointManager syncpoint_manager;
Tegra::MaxwellDeviceMemoryManager memory_manager;
Tegra::MemoryManager gmmu_manager;
std::unique_ptr<Common::FlatAllocator<u32, 0, 32>> allocator;
std::unordered_map<s32, std::unique_ptr<CDmaPusher>> devices;
s32 last_nvdec_fd{};
};
} // namespace Tegra::Host1x
} // namespace Host1x
} // namespace Tegra

View File

@@ -2,12 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/h264.h"
#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/nvdec.h"
@@ -16,68 +10,37 @@ namespace Tegra::Host1x {
#define NVDEC_REG_INDEX(field_name) \
(offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt)
: CDmaPusher{host1x_, id_}, id{id_}, syncpoint{syncpt} {
LOG_INFO(HW_GPU, "Created nvdec {}", id);
}
Nvdec::Nvdec(Host1x& host1x_)
: host1x(host1x_), state{}, codec(std::make_unique<Codec>(host1x, state)) {}
Nvdec::~Nvdec() {
LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
}
Nvdec::~Nvdec() = default;
void Nvdec::ProcessMethod(u32 method, u32 argument) {
regs.reg_array[method] = argument;
state.reg_array[method] = static_cast<u64>(argument) << 8;
switch (method) {
case NVDEC_REG_INDEX(set_codec_id):
CreateDecoder(static_cast<NvdecCommon::VideoCodec>(argument));
codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
break;
case NVDEC_REG_INDEX(execute): {
if (wait_needed) {
std::this_thread::sleep_for(std::chrono::milliseconds(32));
wait_needed = false;
}
case NVDEC_REG_INDEX(execute):
Execute();
} break;
break;
}
}
void Nvdec::CreateDecoder(NvdecCommon::VideoCodec codec) {
if (decoder.get()) {
return;
}
switch (codec) {
case NvdecCommon::VideoCodec::H264:
decoder = std::make_unique<Decoders::H264>(host1x, regs, id);
break;
case NvdecCommon::VideoCodec::VP8:
decoder = std::make_unique<Decoders::VP8>(host1x, regs, id);
break;
case NvdecCommon::VideoCodec::VP9:
decoder = std::make_unique<Decoders::VP9>(host1x, regs, id);
break;
default:
UNIMPLEMENTED_MSG("Codec {}", decoder->GetCurrentCodecName());
break;
}
LOG_INFO(HW_GPU, "Created decoder {} for id {}", decoder->GetCurrentCodecName(), id);
std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
return codec->GetCurrentFrame();
}
void Nvdec::Execute() {
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Off) [[unlikely]] {
// Signalling syncpts too fast can cause games to get stuck as they don't expect a <1ms
// execution time. Sleep for half of a 60 fps frame just in case.
std::this_thread::sleep_for(std::chrono::milliseconds(8));
return;
}
switch (decoder->GetCurrentCodec()) {
switch (codec->GetCurrentCodec()) {
case NvdecCommon::VideoCodec::H264:
case NvdecCommon::VideoCodec::VP8:
case NvdecCommon::VideoCodec::VP9:
decoder->Decode();
codec->Decode();
break;
default:
UNIMPLEMENTED_MSG("Codec {}", decoder->GetCurrentCodecName());
UNIMPLEMENTED_MSG("Codec {}", codec->GetCurrentCodecName());
break;
}
}

View File

@@ -5,49 +5,33 @@
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "video_core/cdma_pusher.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codecs/codec.h"
namespace Tegra {
namespace Host1x {
class Host1x;
class Nvdec final : public CDmaPusher {
class Nvdec {
public:
explicit Nvdec(Host1x& host1x, s32 id, u32 syncpt);
explicit Nvdec(Host1x& host1x);
~Nvdec();
/// Writes the method into the state, Invoke Execute() if encountered
void ProcessMethod(u32 method, u32 arg) override;
void ProcessMethod(u32 method, u32 argument);
std::shared_ptr<FFmpeg::Frame> GetFrame(u64 luma_offset) {
return decoder->GetFrame(luma_offset);
}
u32 GetSyncpoint() const {
return syncpoint;
}
void SetWait() {
wait_needed = true;
}
/// Return most recently decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();
private:
/// Create the decoder when the codec id is set
void CreateDecoder(NvdecCommon::VideoCodec codec);
/// Invoke codec to decode a frame
void Execute();
s32 id;
u32 syncpoint;
NvdecCommon::NvdecRegisters regs{};
std::unique_ptr<Decoder> decoder;
bool wait_needed{false};
Host1x& host1x;
NvdecCommon::NvdecRegisters state;
std::unique_ptr<Codec> codec;
};
} // namespace Host1x

View File

@@ -17,17 +17,6 @@ enum class VideoCodec : u64 {
VP9 = 0x9,
};
struct Offset {
constexpr u64 Address() const noexcept {
return offset << 8;
}
private:
u64 offset;
};
static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
static_assert(sizeof(Offset) == 0x8, "Offset has the wrong size!");
// NVDEC should use a 32-bit address space, but is mapped to 64-bit,
// doubling the sizes here is compensating for that.
struct NvdecRegisters {
@@ -49,40 +38,29 @@ struct NvdecRegisters {
BitField<17, 1, u64> all_intra_frame;
};
} control_params;
Offset picture_info_offset; ///< 0x0808
Offset frame_bitstream_offset; ///< 0x0810
u64 frame_number; ///< 0x0818
Offset h264_slice_data_offsets; ///< 0x0820
Offset h264_mv_dump_offset; ///< 0x0828
INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830
Offset frame_stats_offset; ///< 0x0848
Offset h264_last_surface_luma_offset; ///< 0x0850
Offset h264_last_surface_chroma_offset; ///< 0x0858
std::array<Offset, 17> surface_luma_offsets; ///< 0x0860
std::array<Offset, 17> surface_chroma_offsets; ///< 0x08E8
Offset pic_scratch_buf_offset; ///< 0x0970
Offset external_mvbuffer_offset; ///< 0x0978
INSERT_PADDING_WORDS_NOINIT(32); ///< 0x0980
Offset h264_mbhist_buffer_offset; ///< 0x0A00
INSERT_PADDING_WORDS_NOINIT(30); ///< 0x0A08
Offset vp8_prob_data_offset; ///< 0x0A80
Offset vp8_header_partition_buf_offset; ///< 0x0A88
INSERT_PADDING_WORDS_NOINIT(28); ///< 0x0A90
Offset hvec_scalist_list_offset; ///< 0x0B00
Offset hvec_tile_sizes_offset; ///< 0x0B08
Offset hvec_filter_buffer_offset; ///< 0x0B10
Offset hvec_sao_buffer_offset; ///< 0x0B18
Offset hvec_slice_info_buffer_offset; ///< 0x0B20
Offset hvec_slice_group_index_offset; ///< 0x0B28
INSERT_PADDING_WORDS_NOINIT(20); ///< 0x0B30
Offset vp9_prob_tab_buffer_offset; ///< 0x0B80
Offset vp9_ctx_counter_buffer_offset; ///< 0x0B88
Offset vp9_segment_read_buffer_offset; ///< 0x0B90
Offset vp9_segment_write_buffer_offset; ///< 0x0B98
Offset vp9_tile_size_buffer_offset; ///< 0x0BA0
Offset vp9_col_mvwrite_buffer_offset; ///< 0x0BA8
Offset vp9_col_mvread_buffer_offset; ///< 0x0BB0
Offset vp9_filter_buffer_offset; ///< 0x0BB8
u64 picture_info_offset; ///< 0x0808
u64 frame_bitstream_offset; ///< 0x0810
u64 frame_number; ///< 0x0818
u64 h264_slice_data_offsets; ///< 0x0820
u64 h264_mv_dump_offset; ///< 0x0828
INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830
u64 frame_stats_offset; ///< 0x0848
u64 h264_last_surface_luma_offset; ///< 0x0850
u64 h264_last_surface_chroma_offset; ///< 0x0858
std::array<u64, 17> surface_luma_offset; ///< 0x0860
std::array<u64, 17> surface_chroma_offset; ///< 0x08E8
INSERT_PADDING_WORDS_NOINIT(68); ///< 0x0970
u64 vp8_prob_data_offset; ///< 0x0A80
u64 vp8_header_partition_buf_offset; ///< 0x0A88
INSERT_PADDING_WORDS_NOINIT(60); ///< 0x0A90
u64 vp9_entropy_probs_offset; ///< 0x0B80
u64 vp9_backward_updates_offset; ///< 0x0B88
u64 vp9_last_frame_segmap_offset; ///< 0x0B90
u64 vp9_curr_frame_segmap_offset; ///< 0x0B98
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BA0
u64 vp9_last_frame_mvs_offset; ///< 0x0BA8
u64 vp9_curr_frame_mvs_offset; ///< 0x0BB0
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BB8
};
std::array<u64, NUM_REGS> reg_array;
};
@@ -103,16 +81,16 @@ ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104);
ASSERT_REG_POSITION(frame_stats_offset, 0x109);
ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A);
ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B);
ASSERT_REG_POSITION(surface_luma_offsets, 0x10C);
ASSERT_REG_POSITION(surface_chroma_offsets, 0x11D);
ASSERT_REG_POSITION(surface_luma_offset, 0x10C);
ASSERT_REG_POSITION(surface_chroma_offset, 0x11D);
ASSERT_REG_POSITION(vp8_prob_data_offset, 0x150);
ASSERT_REG_POSITION(vp8_header_partition_buf_offset, 0x151);
ASSERT_REG_POSITION(vp9_prob_tab_buffer_offset, 0x170);
ASSERT_REG_POSITION(vp9_ctx_counter_buffer_offset, 0x171);
ASSERT_REG_POSITION(vp9_segment_read_buffer_offset, 0x172);
ASSERT_REG_POSITION(vp9_segment_write_buffer_offset, 0x173);
ASSERT_REG_POSITION(vp9_col_mvwrite_buffer_offset, 0x175);
ASSERT_REG_POSITION(vp9_col_mvread_buffer_offset, 0x176);
ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170);
ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171);
ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172);
ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173);
ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175);
ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);
#undef ASSERT_REG_POSITION

Some files were not shown because too many files have changed in this diff Show More