Compare commits
4 Commits
57ebe06b7b
...
a0f3865951
Author | SHA1 | Date |
---|---|---|
Radosław Gliński | a0f3865951 | |
Gliniak | 0474053931 | |
Gliniak | 85695692a7 | |
Gliniak | 656b80a93e |
|
@ -31,7 +31,6 @@
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/gpu/command_processor.h"
|
#include "xenia/gpu/command_processor.h"
|
||||||
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
#include "xenia/hid/input_system.h"
|
#include "xenia/hid/input_system.h"
|
||||||
#include "xenia/kernel/xam/profile_manager.h"
|
#include "xenia/kernel/xam/profile_manager.h"
|
||||||
|
@ -58,7 +57,9 @@ DECLARE_bool(guide_button);
|
||||||
|
|
||||||
DECLARE_bool(clear_memory_page_state);
|
DECLARE_bool(clear_memory_page_state);
|
||||||
|
|
||||||
DECLARE_bool(d3d12_readback_resolve);
|
DECLARE_bool(readback_resolve);
|
||||||
|
|
||||||
|
DECLARE_bool(readback_memexport);
|
||||||
|
|
||||||
DEFINE_bool(fullscreen, false, "Whether to launch the emulator in fullscreen.",
|
DEFINE_bool(fullscreen, false, "Whether to launch the emulator in fullscreen.",
|
||||||
"Display");
|
"Display");
|
||||||
|
@ -1674,7 +1675,7 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case ButtonFunctions::ClearMemoryPageState:
|
case ButtonFunctions::ClearMemoryPageState:
|
||||||
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
ToggleGPUSetting(GPUSetting::ClearMemoryPageState);
|
||||||
|
|
||||||
// Assume the user wants ClearCaches as well
|
// Assume the user wants ClearCaches as well
|
||||||
if (cvars::clear_memory_page_state) {
|
if (cvars::clear_memory_page_state) {
|
||||||
|
@ -1689,10 +1690,10 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
break;
|
break;
|
||||||
case ButtonFunctions::ReadbackResolve:
|
case ButtonFunctions::ReadbackResolve:
|
||||||
ToggleGPUSetting(gpu_cvar::ReadbackResolve);
|
ToggleGPUSetting(GPUSetting::ReadbackResolve);
|
||||||
|
|
||||||
notificationTitle = "Toggle Readback Resolve";
|
notificationTitle = "Toggle Readback Resolve";
|
||||||
notificationDesc = cvars::d3d12_readback_resolve ? "Enabled" : "Disabled";
|
notificationDesc = cvars::readback_resolve ? "Enabled" : "Disabled";
|
||||||
|
|
||||||
// Extra Sleep
|
// Extra Sleep
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
|
@ -1862,15 +1863,17 @@ void EmulatorWindow::GamepadHotKeys() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatorWindow::ToggleGPUSetting(gpu_cvar value) {
|
void EmulatorWindow::ToggleGPUSetting(gpu::GPUSetting setting) {
|
||||||
switch (value) {
|
switch (setting) {
|
||||||
case gpu_cvar::ClearMemoryPageState:
|
case GPUSetting::ClearMemoryPageState:
|
||||||
CommonSaveGPUSetting(CommonGPUSetting::ClearMemoryPageState,
|
SaveGPUSetting(GPUSetting::ClearMemoryPageState,
|
||||||
!cvars::clear_memory_page_state);
|
!cvars::clear_memory_page_state);
|
||||||
break;
|
break;
|
||||||
case gpu_cvar::ReadbackResolve:
|
case GPUSetting::ReadbackResolve:
|
||||||
D3D12SaveGPUSetting(D3D12GPUSetting::ReadbackResolve,
|
SaveGPUSetting(GPUSetting::ReadbackResolve, !cvars::readback_resolve);
|
||||||
!cvars::d3d12_readback_resolve);
|
break;
|
||||||
|
case GPUSetting::ReadbackMemexport:
|
||||||
|
SaveGPUSetting(GPUSetting::ReadbackMemexport, !cvars::readback_memexport);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1915,7 +1918,7 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
||||||
msg += "\n";
|
msg += "\n";
|
||||||
|
|
||||||
msg += "Readback Resolve: " +
|
msg += "Readback Resolve: " +
|
||||||
xe::string_util::BoolToString(cvars::d3d12_readback_resolve);
|
xe::string_util::BoolToString(cvars::readback_resolve);
|
||||||
msg += "\n";
|
msg += "\n";
|
||||||
|
|
||||||
msg += "Clear Memory Page State: " +
|
msg += "Clear Memory Page State: " +
|
||||||
|
|
|
@ -112,11 +112,6 @@ class EmulatorWindow {
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class gpu_cvar {
|
|
||||||
ClearMemoryPageState,
|
|
||||||
ReadbackResolve,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ControllerHotKey {
|
class ControllerHotKey {
|
||||||
public:
|
public:
|
||||||
// If true the hotkey can be activated while a title is running, otherwise
|
// If true the hotkey can be activated while a title is running, otherwise
|
||||||
|
@ -235,7 +230,7 @@ class EmulatorWindow {
|
||||||
void VibrateController(xe::hid::InputSystem* input_sys, uint32_t user_index,
|
void VibrateController(xe::hid::InputSystem* input_sys, uint32_t user_index,
|
||||||
bool vibrate = true);
|
bool vibrate = true);
|
||||||
void GamepadHotKeys();
|
void GamepadHotKeys();
|
||||||
void ToggleGPUSetting(gpu_cvar index);
|
void ToggleGPUSetting(gpu::GPUSetting setting);
|
||||||
void DisplayHotKeysConfig();
|
void DisplayHotKeysConfig();
|
||||||
|
|
||||||
static std::string CanonicalizeFileExtension(
|
static std::string CanonicalizeFileExtension(
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2024 Xenia Canary. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_BASE_SPINLOCK_H_
|
||||||
|
#define XENIA_BASE_SPINLOCK_H_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
class spinlock {
|
||||||
|
public:
|
||||||
|
void lock() {
|
||||||
|
while (locked.test_and_set(std::memory_order_acquire)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void unlock() { locked.clear(std::memory_order_release); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic_flag locked = ATOMIC_FLAG_INIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xe
|
||||||
|
#endif
|
|
@ -402,7 +402,7 @@ class Timer : public WaitHandle {
|
||||||
virtual bool Cancel() = 0;
|
virtual bool Cancel() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if XE_PLATFORM_WINDOWS
|
#if XE_PLATFORM_WIN32
|
||||||
struct ThreadPriority {
|
struct ThreadPriority {
|
||||||
static const int32_t kLowest = -2;
|
static const int32_t kLowest = -2;
|
||||||
static const int32_t kBelowNormal = -1;
|
static const int32_t kBelowNormal = -1;
|
||||||
|
|
|
@ -50,17 +50,52 @@ DEFINE_bool(clear_memory_page_state, false,
|
||||||
"for 'Team Ninja' Games to fix missing character models)",
|
"for 'Team Ninja' Games to fix missing character models)",
|
||||||
"GPU");
|
"GPU");
|
||||||
|
|
||||||
|
DEFINE_bool(
|
||||||
|
readback_resolve, false,
|
||||||
|
"[D3D12 Only] Read render-to-texture results on the CPU. This may be "
|
||||||
|
"needed in some games, for instance, for screenshots in saved games, but "
|
||||||
|
"causes mid-frame synchronization, so it has a huge performance impact.",
|
||||||
|
"GPU");
|
||||||
|
|
||||||
|
DEFINE_bool(
|
||||||
|
readback_memexport, false,
|
||||||
|
"[D3D12 Only] Read data written by memory export in shaders on the CPU. "
|
||||||
|
"This may be needed in some games (but many only access exported data on "
|
||||||
|
"the GPU, and this flag isn't needed to handle such behavior), but causes "
|
||||||
|
"mid-frame synchronization, so it has a huge performance impact.",
|
||||||
|
"GPU");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
void CommonSaveGPUSetting(CommonGPUSetting setting, uint64_t value) {
|
// This should be written completely differently with support for different
|
||||||
|
// types.
|
||||||
|
void SaveGPUSetting(GPUSetting setting, uint64_t value) {
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case CommonGPUSetting::ClearMemoryPageState:
|
case GPUSetting::ClearMemoryPageState:
|
||||||
OVERRIDE_bool(clear_memory_page_state, (bool)value);
|
OVERRIDE_bool(clear_memory_page_state, static_cast<bool>(value));
|
||||||
|
break;
|
||||||
|
case GPUSetting::ReadbackResolve:
|
||||||
|
OVERRIDE_bool(readback_resolve, static_cast<bool>(value));
|
||||||
|
break;
|
||||||
|
case GPUSetting::ReadbackMemexport:
|
||||||
|
OVERRIDE_bool(readback_memexport, static_cast<bool>(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetGPUSetting(GPUSetting setting) {
|
||||||
|
switch (setting) {
|
||||||
|
case GPUSetting::ClearMemoryPageState:
|
||||||
|
return cvars::clear_memory_page_state;
|
||||||
|
case GPUSetting::ReadbackResolve:
|
||||||
|
return cvars::readback_resolve;
|
||||||
|
case GPUSetting::ReadbackMemexport:
|
||||||
|
return cvars::readback_memexport;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace xe::gpu::xenos;
|
using namespace xe::gpu::xenos;
|
||||||
|
|
||||||
CommandProcessor::CommandProcessor(GraphicsSystem* graphics_system,
|
CommandProcessor::CommandProcessor(GraphicsSystem* graphics_system,
|
||||||
|
|
|
@ -33,11 +33,14 @@ class ByteStream;
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
enum class CommonGPUSetting {
|
enum class GPUSetting {
|
||||||
ClearMemoryPageState,
|
ClearMemoryPageState,
|
||||||
|
ReadbackResolve,
|
||||||
|
ReadbackMemexport
|
||||||
};
|
};
|
||||||
|
|
||||||
void CommonSaveGPUSetting(CommonGPUSetting setting, uint64_t value);
|
void SaveGPUSetting(GPUSetting setting, uint64_t value);
|
||||||
|
bool GetGPUSetting(GPUSetting setting);
|
||||||
|
|
||||||
class GraphicsSystem;
|
class GraphicsSystem;
|
||||||
class Shader;
|
class Shader;
|
||||||
|
|
|
@ -32,19 +32,7 @@ DEFINE_bool(d3d12_bindless, true,
|
||||||
"Use bindless resources where available - may improve performance, "
|
"Use bindless resources where available - may improve performance, "
|
||||||
"but may make debugging more complicated.",
|
"but may make debugging more complicated.",
|
||||||
"D3D12");
|
"D3D12");
|
||||||
DEFINE_bool(d3d12_readback_memexport, false,
|
|
||||||
"Read data written by memory export in shaders on the CPU. This "
|
|
||||||
"may be needed in some games (but many only access exported data "
|
|
||||||
"on the GPU, and this flag isn't needed to handle such behavior), "
|
|
||||||
"but causes mid-frame synchronization, so it has a huge "
|
|
||||||
"performance impact.",
|
|
||||||
"D3D12");
|
|
||||||
DEFINE_bool(d3d12_readback_resolve, false,
|
|
||||||
"Read render-to-texture results on the CPU. This may be needed in "
|
|
||||||
"some games, for instance, for screenshots in saved games, but "
|
|
||||||
"causes mid-frame synchronization, so it has a huge performance "
|
|
||||||
"impact.",
|
|
||||||
"D3D12");
|
|
||||||
DEFINE_bool(d3d12_submit_on_primary_buffer_end, true,
|
DEFINE_bool(d3d12_submit_on_primary_buffer_end, true,
|
||||||
"Submit the command list when a PM4 primary buffer ends if it's "
|
"Submit the command list when a PM4 primary buffer ends if it's "
|
||||||
"possible to submit immediately to try to reduce frame latency.",
|
"possible to submit immediately to try to reduce frame latency.",
|
||||||
|
@ -54,15 +42,6 @@ DECLARE_bool(clear_memory_page_state);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
void D3D12SaveGPUSetting(D3D12GPUSetting setting, uint64_t value) {
|
|
||||||
switch (setting) {
|
|
||||||
case D3D12GPUSetting::ReadbackResolve:
|
|
||||||
OVERRIDE_bool(d3d12_readback_resolve, (bool)value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace d3d12 {
|
namespace d3d12 {
|
||||||
|
|
||||||
// Generated with `xb buildshaders`.
|
// Generated with `xb buildshaders`.
|
||||||
|
@ -3011,7 +2990,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||||
memexport_range.base_address_dwords << 2, memexport_range.size_bytes,
|
memexport_range.base_address_dwords << 2, memexport_range.size_bytes,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
if (cvars::d3d12_readback_memexport) {
|
if (GetGPUSetting(GPUSetting::ReadbackResolve)) {
|
||||||
// Read the exported data on the CPU.
|
// Read the exported data on the CPU.
|
||||||
uint32_t memexport_total_size = 0;
|
uint32_t memexport_total_size = 0;
|
||||||
for (const draw_util::MemExportRange& memexport_range :
|
for (const draw_util::MemExportRange& memexport_range :
|
||||||
|
@ -3091,7 +3070,7 @@ bool D3D12CommandProcessor::IssueCopy() {
|
||||||
if (!BeginSubmission(true)) {
|
if (!BeginSubmission(true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!cvars::d3d12_readback_resolve) {
|
if (!GetGPUSetting(GPUSetting::ReadbackResolve)) {
|
||||||
uint32_t written_address, written_length;
|
uint32_t written_address, written_length;
|
||||||
return render_target_cache_->Resolve(*memory_, *shared_memory_,
|
return render_target_cache_->Resolve(*memory_, *shared_memory_,
|
||||||
*texture_cache_, written_address,
|
*texture_cache_, written_address,
|
||||||
|
|
|
@ -710,7 +710,7 @@ void KernelState::UnloadUserModule(const object_ref<UserModule>& module,
|
||||||
return e->path() == module->path();
|
return e->path() == module->path();
|
||||||
}) == user_modules_.end());
|
}) == user_modules_.end());
|
||||||
|
|
||||||
object_table()->ReleaseHandleInLock(module->handle());
|
object_table()->ReleaseHandle(module->handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::TerminateTitle() {
|
void KernelState::TerminateTitle() {
|
||||||
|
|
|
@ -26,132 +26,104 @@ ObjectTable::ObjectTable() {}
|
||||||
ObjectTable::~ObjectTable() { Reset(); }
|
ObjectTable::~ObjectTable() { Reset(); }
|
||||||
|
|
||||||
void ObjectTable::Reset() {
|
void ObjectTable::Reset() {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
|
|
||||||
// Release all objects.
|
host_object_table_.Reset();
|
||||||
for (uint32_t n = 0; n < table_capacity_; n++) {
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
ObjectTableEntry& entry = table_[n];
|
table.Reset();
|
||||||
if (entry.object) {
|
|
||||||
entry.object->Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (uint32_t n = 0; n < host_table_capacity_; n++) {
|
|
||||||
ObjectTableEntry& entry = host_table_[n];
|
|
||||||
if (entry.object) {
|
|
||||||
entry.object->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table_capacity_ = 0;
|
|
||||||
host_table_capacity_ = 0;
|
|
||||||
last_free_entry_ = 0;
|
|
||||||
last_free_host_entry_ = 0;
|
|
||||||
free(table_);
|
|
||||||
table_ = nullptr;
|
|
||||||
free(host_table_);
|
|
||||||
host_table_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::FindFreeSlot(uint32_t* out_slot, bool host) {
|
uint32_t ObjectTable::GetFirstFreeSlot(
|
||||||
// Find a free slot.
|
ObjectTable::ObjectTableInfo* const table) {
|
||||||
uint32_t slot = host ? last_free_host_entry_ : last_free_entry_;
|
// Check if
|
||||||
uint32_t capacity = host ? host_table_capacity_ : table_capacity_;
|
if (!table->freed_table_slots_.empty()) {
|
||||||
uint32_t scan_count = 0;
|
uint32_t slot = table->freed_table_slots_.front();
|
||||||
while (scan_count < capacity) {
|
table->freed_table_slots_.erase(table->freed_table_slots_.begin());
|
||||||
ObjectTableEntry& entry = host ? host_table_[slot] : table_[slot];
|
return slot;
|
||||||
if (!entry.object) {
|
}
|
||||||
*out_slot = slot;
|
// Check if latest used slot is free again.
|
||||||
return X_STATUS_SUCCESS;
|
ObjectTableEntry& entry = table->table_[table->previous_free_slot_];
|
||||||
}
|
if (!entry.object) {
|
||||||
scan_count++;
|
return table->previous_free_slot_;
|
||||||
slot = (slot + 1) % capacity;
|
|
||||||
if (slot == 0 && host) {
|
|
||||||
// Never allow 0 handles.
|
|
||||||
scan_count++;
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table out of slots, expand.
|
if (++table->previous_free_slot_ >= table->table_.size()) {
|
||||||
uint32_t new_table_capacity = std::max(16 * 1024u, capacity * 2);
|
table->table_.reserve(table->table_.size() * 2);
|
||||||
if (!Resize(new_table_capacity, host)) {
|
|
||||||
return X_STATUS_NO_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Never allow 0 handles on host.
|
return table->previous_free_slot_;
|
||||||
slot = host ? ++last_free_host_entry_ : last_free_entry_++;
|
}
|
||||||
*out_slot = slot;
|
|
||||||
|
|
||||||
|
X_STATUS ObjectTable::FindFreeSlot(const XObject* const object,
|
||||||
|
uint32_t* out_slot) {
|
||||||
|
auto object_table = GetTableForObject(object);
|
||||||
|
|
||||||
|
*out_slot = GetFirstFreeSlot(object_table);
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Resize(uint32_t new_capacity, bool host) {
|
ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject(
|
||||||
uint32_t capacity = host ? host_table_capacity_ : table_capacity_;
|
const XObject* const obj) {
|
||||||
uint32_t new_size = new_capacity * sizeof(ObjectTableEntry);
|
if (obj->is_host_object()) {
|
||||||
uint32_t old_size = capacity * sizeof(ObjectTableEntry);
|
return &host_object_table_;
|
||||||
auto new_table = reinterpret_cast<ObjectTableEntry*>(
|
|
||||||
realloc(host ? host_table_ : table_, new_size));
|
|
||||||
if (!new_table) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero out new entries.
|
if (obj->type() == XObject::Type::Thread) {
|
||||||
if (new_size > old_size) {
|
// Switcharoo for title/system thread!
|
||||||
std::memset(reinterpret_cast<uint8_t*>(new_table) + old_size, 0,
|
return &guest_object_table_[kGuestHandleTitleThreadBase];
|
||||||
new_size - old_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host) {
|
return &guest_object_table_[kGuestHandleBase];
|
||||||
last_free_host_entry_ = capacity;
|
}
|
||||||
host_table_capacity_ = new_capacity;
|
|
||||||
host_table_ = new_table;
|
ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject(
|
||||||
} else {
|
const X_HANDLE handle) {
|
||||||
last_free_entry_ = capacity;
|
if (!handle) {
|
||||||
table_capacity_ = new_capacity;
|
return nullptr;
|
||||||
table_ = new_table;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if (handle & 0x00FF0000) {
|
||||||
|
return &guest_object_table_[kGuestHandleBase];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t handle_mask = handle & 0xFF000000;
|
||||||
|
|
||||||
|
if (handle_mask == kGuestHandleTitleThreadBase) {
|
||||||
|
return &guest_object_table_[kGuestHandleTitleThreadBase];
|
||||||
|
}
|
||||||
|
if (handle_mask == kGuestHandleSystemThreadBase) {
|
||||||
|
return &guest_object_table_[kGuestHandleSystemThreadBase];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only host check remains
|
||||||
|
return &host_object_table_;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
|
|
||||||
uint32_t handle = 0;
|
uint32_t handle = 0;
|
||||||
{
|
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
|
||||||
|
|
||||||
// Find a free slot.
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
uint32_t slot = 0;
|
|
||||||
bool host_object = object->is_host_object();
|
|
||||||
result = FindFreeSlot(&slot, host_object);
|
|
||||||
|
|
||||||
// Stash.
|
auto table = GetTableForObject(object);
|
||||||
if (XSUCCEEDED(result)) {
|
uint32_t slot = GetFirstFreeSlot(table);
|
||||||
ObjectTableEntry& entry = host_object ? host_table_[slot] : table_[slot];
|
|
||||||
entry.object = object;
|
|
||||||
entry.handle_ref_count = 1;
|
|
||||||
handle = slot << 2;
|
|
||||||
if (!host_object) {
|
|
||||||
if (object->type() != XObject::Type::Socket) {
|
|
||||||
handle += XObject::kHandleBase;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handle += XObject::kHandleHostBase;
|
|
||||||
}
|
|
||||||
object->handles().push_back(handle);
|
|
||||||
|
|
||||||
// Retain so long as the object is in the table.
|
ObjectTableEntry& entry = table->table_[slot];
|
||||||
object->Retain();
|
|
||||||
|
|
||||||
XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name());
|
entry.object = object;
|
||||||
}
|
entry.handle_ref_count = 1;
|
||||||
}
|
handle = table->GetSlotHandle(slot);
|
||||||
|
object->handles().push_back(handle);
|
||||||
|
|
||||||
if (XSUCCEEDED(result)) {
|
// Retain so long as the object is in the table.
|
||||||
if (out_handle) {
|
object->Retain();
|
||||||
*out_handle = handle;
|
|
||||||
}
|
XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name());
|
||||||
|
|
||||||
|
if (out_handle) {
|
||||||
|
*out_handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -161,21 +133,40 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) {
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
handle = TranslateHandle(handle);
|
handle = TranslateHandle(handle);
|
||||||
|
|
||||||
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
|
// For whatever reason all duplicates are going into base mask even threads.
|
||||||
|
auto table = &guest_object_table_[kGuestHandleBase];
|
||||||
|
uint32_t slot = GetFirstFreeSlot(table);
|
||||||
|
|
||||||
XObject* object = LookupObject(handle, false);
|
XObject* object = LookupObject(handle, false);
|
||||||
|
|
||||||
if (object) {
|
if (object) {
|
||||||
result = AddHandle(object, out_handle);
|
ObjectTableEntry& entry = table->table_[slot];
|
||||||
object->Release(); // Release the ref that LookupObject took
|
entry.object = object;
|
||||||
|
entry.handle_ref_count = 1;
|
||||||
|
*out_handle = table->GetSlotHandle(slot);
|
||||||
|
object->handles().push_back(*out_handle);
|
||||||
|
|
||||||
|
// Retain so long as the object is in the table.
|
||||||
|
object->Retain();
|
||||||
|
|
||||||
|
XELOGI("Duplicated handle:{:08X} to {:08X} for {}", handle, *out_handle,
|
||||||
|
typeid(*object).name());
|
||||||
} else {
|
} else {
|
||||||
result = X_STATUS_INVALID_HANDLE;
|
result = X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
handle = TranslateHandle(handle);
|
||||||
|
if (!handle) {
|
||||||
|
return X_STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
|
|
||||||
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
@ -185,17 +176,19 @@ X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
handle = TranslateHandle(handle);
|
||||||
|
if (!handle) {
|
||||||
|
return X_STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
return ReleaseHandleInLock(handle);
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
}
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) {
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--entry->handle_ref_count == 0) {
|
if (--entry->handle_ref_count == 0) {
|
||||||
|
lock.~lock_guard();
|
||||||
// No more references. Remove it from the table.
|
// No more references. Remove it from the table.
|
||||||
return RemoveHandle(handle);
|
return RemoveHandle(handle);
|
||||||
}
|
}
|
||||||
|
@ -204,6 +197,7 @@ X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) {
|
||||||
// (but not a failure code)
|
// (but not a failure code)
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
@ -211,9 +205,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
|
||||||
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
@ -231,6 +226,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
object->handles().erase(handle_entry);
|
object->handles().erase(handle_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto table = GetTableForObject(handle);
|
||||||
|
const uint32_t slot = table->GetHandleSlot(handle);
|
||||||
|
table->freed_table_slots_.push_back(slot);
|
||||||
|
|
||||||
XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name());
|
XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name());
|
||||||
|
|
||||||
// Remove object name from mapping to prevent naming collision.
|
// Remove object name from mapping to prevent naming collision.
|
||||||
|
@ -240,69 +239,21 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
// Release now that the object has been removed from the table.
|
// Release now that the object has been removed from the table.
|
||||||
object->Release();
|
object->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<object_ref<XObject>> ObjectTable::GetAllObjects() {
|
|
||||||
auto lock = global_critical_region_.Acquire();
|
|
||||||
std::vector<object_ref<XObject>> results;
|
|
||||||
|
|
||||||
for (uint32_t slot = 0; slot < host_table_capacity_; slot++) {
|
|
||||||
auto& entry = host_table_[slot];
|
|
||||||
if (entry.object && std::find(results.begin(), results.end(),
|
|
||||||
entry.object) == results.end()) {
|
|
||||||
entry.object->Retain();
|
|
||||||
results.push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
|
||||||
auto& entry = table_[slot];
|
|
||||||
if (entry.object && std::find(results.begin(), results.end(),
|
|
||||||
entry.object) == results.end()) {
|
|
||||||
entry.object->Retain();
|
|
||||||
results.push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectTable::PurgeAllObjects() {
|
|
||||||
auto lock = global_critical_region_.Acquire();
|
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
|
||||||
auto& entry = table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
entry.handle_ref_count = 0;
|
|
||||||
entry.object->Release();
|
|
||||||
|
|
||||||
entry.object = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) {
|
ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto table = GetTableForObject(handle);
|
||||||
return LookupTableInLock(handle);
|
if (!table) {
|
||||||
}
|
|
||||||
|
|
||||||
ObjectTable::ObjectTableEntry* ObjectTable::LookupTableInLock(X_HANDLE handle) {
|
|
||||||
handle = TranslateHandle(handle);
|
|
||||||
if (!handle) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
auto* entry = &table->table_[table->GetHandleSlot(handle)];
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
|
||||||
if (is_host_object) {
|
|
||||||
if (slot <= host_table_capacity_) {
|
|
||||||
return &host_table_[slot];
|
|
||||||
}
|
|
||||||
} else if (slot <= table_capacity_) {
|
|
||||||
return &table_[slot];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
if (!entry->object) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic lookup
|
// Generic lookup
|
||||||
|
@ -321,64 +272,78 @@ XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) {
|
||||||
}
|
}
|
||||||
|
|
||||||
XObject* object = nullptr;
|
XObject* object = nullptr;
|
||||||
if (!already_locked) {
|
auto entry = LookupTable(handle);
|
||||||
global_critical_region_.mutex().lock();
|
if (!entry) {
|
||||||
}
|
return nullptr;
|
||||||
|
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
|
||||||
|
|
||||||
// Verify slot.
|
|
||||||
if (is_host_object) {
|
|
||||||
if (slot < host_table_capacity_) {
|
|
||||||
ObjectTableEntry& entry = host_table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
object = entry.object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (slot < table_capacity_) {
|
|
||||||
ObjectTableEntry& entry = table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
object = entry.object;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retain the object pointer.
|
// Retain the object pointer.
|
||||||
if (object) {
|
if (entry->object) {
|
||||||
object->Retain();
|
entry->object->Retain();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!already_locked) {
|
return entry->object;
|
||||||
global_critical_region_.mutex().unlock();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return object;
|
std::vector<object_ref<XObject>> ObjectTable::GetAllObjects() {
|
||||||
|
std::vector<object_ref<XObject>> results;
|
||||||
|
|
||||||
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
|
for (uint32_t i = 0; i < table.previous_free_slot_ + 1; i++) {
|
||||||
|
auto& object = table.table_.at(i).object;
|
||||||
|
|
||||||
|
if (!object) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
object->Retain();
|
||||||
|
results.push_back(object_ref<XObject>(object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectTable::PurgeAllObjects() {
|
||||||
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
|
|
||||||
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
|
for (auto& [_, entry] : table.table_) {
|
||||||
|
if (!entry.object) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.handle_ref_count = 0;
|
||||||
|
entry.object->Release();
|
||||||
|
entry.object = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectTable::GetObjectsByType(XObject::Type type,
|
void ObjectTable::GetObjectsByType(XObject::Type type,
|
||||||
std::vector<object_ref<XObject>>* results) {
|
std::vector<object_ref<XObject>>* results) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
for (uint32_t slot = 0; slot < host_table_capacity_; ++slot) {
|
|
||||||
auto& entry = host_table_[slot];
|
if (type == XObject::Type::Thread) {
|
||||||
if (entry.object) {
|
for (auto& [_, entry] :
|
||||||
if (entry.object->type() == type) {
|
guest_object_table_[kGuestHandleTitleThreadBase].table_) {
|
||||||
|
if (entry.object) {
|
||||||
entry.object->Retain();
|
entry.object->Retain();
|
||||||
results->push_back(object_ref<XObject>(entry.object));
|
results->push_back(object_ref<XObject>(entry.object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; ++slot) {
|
|
||||||
auto& entry = table_[slot];
|
for (auto& [_, entry] : guest_object_table_[kGuestHandleBase].table_) {
|
||||||
if (entry.object) {
|
if (entry.object && entry.object->type() == type) {
|
||||||
if (entry.object->type() == type) {
|
entry.object->Retain();
|
||||||
entry.object->Retain();
|
results->push_back(object_ref<XObject>(entry.object));
|
||||||
results->push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
|
X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) const {
|
||||||
// chrispy: reordered these by likelihood, most likely case is that handle is
|
// chrispy: reordered these by likelihood, most likely case is that handle is
|
||||||
// not a special handle
|
// not a special handle
|
||||||
XE_LIKELY_IF(handle < 0xFFFFFFFE) { return handle; }
|
XE_LIKELY_IF(handle < 0xFFFFFFFE) { return handle; }
|
||||||
|
@ -390,31 +355,31 @@ X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name mapping is available only for guest objects!
|
||||||
X_STATUS ObjectTable::AddNameMapping(const std::string_view name,
|
X_STATUS ObjectTable::AddNameMapping(const std::string_view name,
|
||||||
X_HANDLE handle) {
|
X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
if (name_table_.count(string_key_case(name))) {
|
if (guest_name_table_.count(string_key_case(name))) {
|
||||||
return X_STATUS_OBJECT_NAME_COLLISION;
|
return X_STATUS_OBJECT_NAME_COLLISION;
|
||||||
}
|
}
|
||||||
name_table_.insert({string_key_case::create(name), handle});
|
guest_name_table_.insert({string_key_case::create(name), handle});
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectTable::RemoveNameMapping(const std::string_view name) {
|
void ObjectTable::RemoveNameMapping(const std::string_view name) {
|
||||||
// Names are case-insensitive.
|
// Names are case-insensitive.
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto it = guest_name_table_.find(string_key_case(name));
|
||||||
auto it = name_table_.find(string_key_case(name));
|
if (it != guest_name_table_.end()) {
|
||||||
if (it != name_table_.end()) {
|
guest_name_table_.erase(it);
|
||||||
name_table_.erase(it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
||||||
X_HANDLE* out_handle) {
|
X_HANDLE* out_handle) {
|
||||||
// Names are case-insensitive.
|
// Names are case-insensitive.
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
std::lock_guard<xe::spinlock> lock(spinlock_);
|
||||||
auto it = name_table_.find(string_key_case(name));
|
auto it = guest_name_table_.find(string_key_case(name));
|
||||||
if (it == name_table_.end()) {
|
if (it == guest_name_table_.end()) {
|
||||||
*out_handle = X_INVALID_HANDLE_VALUE;
|
*out_handle = X_INVALID_HANDLE_VALUE;
|
||||||
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -431,6 +396,7 @@ X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Save(ByteStream* stream) {
|
bool ObjectTable::Save(ByteStream* stream) {
|
||||||
|
/*
|
||||||
stream->Write<uint32_t>(host_table_capacity_);
|
stream->Write<uint32_t>(host_table_capacity_);
|
||||||
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
||||||
auto& entry = host_table_[i];
|
auto& entry = host_table_[i];
|
||||||
|
@ -442,11 +408,12 @@ bool ObjectTable::Save(ByteStream* stream) {
|
||||||
auto& entry = table_[i];
|
auto& entry = table_[i];
|
||||||
stream->Write<int32_t>(entry.handle_ref_count);
|
stream->Write<int32_t>(entry.handle_ref_count);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Restore(ByteStream* stream) {
|
bool ObjectTable::Restore(ByteStream* stream) {
|
||||||
|
/*
|
||||||
Resize(stream->Read<uint32_t>(), true);
|
Resize(stream->Read<uint32_t>(), true);
|
||||||
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
||||||
auto& entry = host_table_[i];
|
auto& entry = host_table_[i];
|
||||||
|
@ -460,11 +427,12 @@ bool ObjectTable::Restore(ByteStream* stream) {
|
||||||
// entry.object = nullptr;
|
// entry.object = nullptr;
|
||||||
entry.handle_ref_count = stream->Read<int32_t>();
|
entry.handle_ref_count = stream->Read<int32_t>();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
||||||
|
/*
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
const bool is_host_object = XObject::is_handle_host_object(handle);
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
||||||
uint32_t capacity = is_host_object ? host_table_capacity_ : table_capacity_;
|
uint32_t capacity = is_host_object ? host_table_capacity_ : table_capacity_;
|
||||||
|
@ -475,7 +443,7 @@ X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
||||||
entry.object = object;
|
entry.object = object;
|
||||||
object->Retain();
|
object->Retain();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/mutex.h"
|
#include "xenia/base/mutex.h"
|
||||||
|
#include "xenia/base/spinlock.h"
|
||||||
#include "xenia/base/string_key.h"
|
#include "xenia/base/string_key.h"
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
@ -38,7 +39,6 @@ class ObjectTable {
|
||||||
X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle);
|
X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle);
|
||||||
X_STATUS RetainHandle(X_HANDLE handle);
|
X_STATUS RetainHandle(X_HANDLE handle);
|
||||||
X_STATUS ReleaseHandle(X_HANDLE handle);
|
X_STATUS ReleaseHandle(X_HANDLE handle);
|
||||||
X_STATUS ReleaseHandleInLock(X_HANDLE handle);
|
|
||||||
X_STATUS RemoveHandle(X_HANDLE handle);
|
X_STATUS RemoveHandle(X_HANDLE handle);
|
||||||
|
|
||||||
bool Save(ByteStream* stream);
|
bool Save(ByteStream* stream);
|
||||||
|
@ -59,6 +59,7 @@ class ObjectTable {
|
||||||
|
|
||||||
X_STATUS AddNameMapping(const std::string_view name, X_HANDLE handle);
|
X_STATUS AddNameMapping(const std::string_view name, X_HANDLE handle);
|
||||||
void RemoveNameMapping(const std::string_view name);
|
void RemoveNameMapping(const std::string_view name);
|
||||||
|
|
||||||
X_STATUS GetObjectByName(const std::string_view name, X_HANDLE* out_handle);
|
X_STATUS GetObjectByName(const std::string_view name, X_HANDLE* out_handle);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::vector<object_ref<T>> GetObjectsByType(XObject::Type type) {
|
std::vector<object_ref<T>> GetObjectsByType(XObject::Type type) {
|
||||||
|
@ -85,28 +86,77 @@ class ObjectTable {
|
||||||
int handle_ref_count = 0;
|
int handle_ref_count = 0;
|
||||||
XObject* object = nullptr;
|
XObject* object = nullptr;
|
||||||
};
|
};
|
||||||
ObjectTableEntry* LookupTableInLock(X_HANDLE handle);
|
|
||||||
|
struct ObjectTableInfo {
|
||||||
|
uint32_t table_base_handle_offset_ = 0;
|
||||||
|
uint32_t previous_free_slot_ = 0;
|
||||||
|
std::unordered_map<uint32_t, ObjectTableEntry> table_ = {};
|
||||||
|
std::vector<uint32_t> freed_table_slots_ = {};
|
||||||
|
|
||||||
|
// Ctor for host objects
|
||||||
|
ObjectTableInfo() {
|
||||||
|
previous_free_slot_ = 1;
|
||||||
|
|
||||||
|
freed_table_slots_.reserve(255);
|
||||||
|
table_.reserve(4095);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ctor for guest objects
|
||||||
|
ObjectTableInfo(uint32_t base_handle_offset) {
|
||||||
|
table_base_handle_offset_ = base_handle_offset;
|
||||||
|
|
||||||
|
freed_table_slots_.reserve(255);
|
||||||
|
table_.reserve(4095);
|
||||||
|
};
|
||||||
|
|
||||||
|
X_HANDLE GetSlotHandle(uint32_t slot) {
|
||||||
|
return (slot << 2) + table_base_handle_offset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetHandleSlot(X_HANDLE handle) {
|
||||||
|
return (handle - table_base_handle_offset_) >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
for (auto& [_, entry] : table_) {
|
||||||
|
if (entry.object) {
|
||||||
|
entry.object->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_free_slot_ = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ObjectTableEntry* LookupTable(X_HANDLE handle);
|
ObjectTableEntry* LookupTable(X_HANDLE handle);
|
||||||
XObject* LookupObject(X_HANDLE handle, bool already_locked);
|
XObject* LookupObject(X_HANDLE handle, bool already_locked);
|
||||||
void GetObjectsByType(XObject::Type type,
|
void GetObjectsByType(XObject::Type type,
|
||||||
std::vector<object_ref<XObject>>* results);
|
std::vector<object_ref<XObject>>* results);
|
||||||
|
|
||||||
X_HANDLE TranslateHandle(X_HANDLE handle);
|
X_HANDLE TranslateHandle(X_HANDLE handle) const;
|
||||||
static constexpr uint32_t GetHandleSlot(X_HANDLE handle, bool host) {
|
|
||||||
handle &= host ? ~XObject::kHandleHostBase : ~XObject::kHandleBase;
|
|
||||||
return handle >> 2;
|
|
||||||
}
|
|
||||||
X_STATUS FindFreeSlot(uint32_t* out_slot, bool host);
|
|
||||||
bool Resize(uint32_t new_capacity, bool host);
|
|
||||||
|
|
||||||
xe::global_critical_region global_critical_region_;
|
X_STATUS FindFreeSlot(const XObject* const object, uint32_t* out_slot);
|
||||||
uint32_t table_capacity_ = 0;
|
|
||||||
uint32_t host_table_capacity_ = 0;
|
ObjectTableInfo* const GetTableForObject(const XObject* const obj);
|
||||||
ObjectTableEntry* table_ = nullptr;
|
ObjectTableInfo* const GetTableForObject(const X_HANDLE handle);
|
||||||
ObjectTableEntry* host_table_ = nullptr;
|
uint32_t GetFirstFreeSlot(ObjectTableInfo* const table);
|
||||||
uint32_t last_free_entry_ = 0;
|
|
||||||
uint32_t last_free_host_entry_ = 0;
|
xe::spinlock spinlock_;
|
||||||
std::unordered_map<string_key_case, X_HANDLE> name_table_;
|
|
||||||
|
ObjectTableInfo host_object_table_;
|
||||||
|
|
||||||
|
static constexpr uint32_t kGuestHandleBase = 0x00100000;
|
||||||
|
static constexpr uint32_t kGuestHandleTitleThreadBase = 0xF8000000;
|
||||||
|
static constexpr uint32_t kGuestHandleSystemThreadBase = 0xFB000000;
|
||||||
|
|
||||||
|
std::map<const uint32_t, ObjectTableInfo> guest_object_table_ = {
|
||||||
|
{kGuestHandleBase, ObjectTableInfo(kGuestHandleBase)},
|
||||||
|
{kGuestHandleTitleThreadBase,
|
||||||
|
ObjectTableInfo(kGuestHandleTitleThreadBase)},
|
||||||
|
{kGuestHandleSystemThreadBase,
|
||||||
|
ObjectTableInfo(kGuestHandleSystemThreadBase)}};
|
||||||
|
|
||||||
|
std::unordered_map<string_key_case, X_HANDLE> guest_name_table_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic lookup
|
// Generic lookup
|
||||||
|
|
|
@ -95,12 +95,9 @@ X_STATUS XObject::Delete() {
|
||||||
if (kernel_state_ == nullptr) {
|
if (kernel_state_ == nullptr) {
|
||||||
// Fake return value for api-scanner
|
// Fake return value for api-scanner
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
} else {
|
|
||||||
if (!name_.empty()) {
|
|
||||||
kernel_state_->object_table()->RemoveNameMapping(name_);
|
|
||||||
}
|
|
||||||
return kernel_state_->object_table()->RemoveHandle(handles_[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return kernel_state_->object_table()->RemoveHandle(handles_[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XObject::SaveObject(ByteStream* stream) {
|
bool XObject::SaveObject(ByteStream* stream) {
|
||||||
|
|
|
@ -199,9 +199,6 @@ class XObject {
|
||||||
static object_ref<XObject> Restore(KernelState* kernel_state, Type type,
|
static object_ref<XObject> Restore(KernelState* kernel_state, Type type,
|
||||||
ByteStream* stream);
|
ByteStream* stream);
|
||||||
|
|
||||||
static constexpr bool is_handle_host_object(X_HANDLE handle) {
|
|
||||||
return handle > XObject::kHandleHostBase && handle < XObject::kHandleBase;
|
|
||||||
};
|
|
||||||
// Reference()
|
// Reference()
|
||||||
// Dereference()
|
// Dereference()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue