mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-31 07:43:16 +00:00
video_core: Rewrite vulkan and videoout
This commit is contained in:
parent
0a94899c86
commit
c01b6f8397
89 changed files with 5378 additions and 2150 deletions
|
@ -2,11 +2,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/PS4/GPU/gpu_memory.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "emulator.h"
|
||||
|
||||
namespace Libraries::GnmDriver {
|
||||
|
||||
|
@ -293,7 +291,6 @@ int PS4_SYSV_ABI sceGnmFindResourcesPublic() {
|
|||
|
||||
void PS4_SYSV_ABI sceGnmFlushGarlic() {
|
||||
LOG_WARNING(Lib_GnmDriver, "(STUBBED) called");
|
||||
GPU::flushGarlic(Emu::getGraphicCtx());
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmGetCoredumpAddress() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "core/libraries/kernel/event_queue.h"
|
||||
|
||||
|
@ -11,16 +12,11 @@ EqueueInternal::~EqueueInternal() = default;
|
|||
int EqueueInternal::addEvent(const EqueueEvent& event) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (m_events.size() > 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
ASSERT(m_events.empty());
|
||||
ASSERT(!event.isTriggered);
|
||||
|
||||
// TODO check if event is already exists and return it. Currently we just add in m_events array
|
||||
m_events.push_back(event);
|
||||
|
||||
if (event.isTriggered) {
|
||||
BREAKPOINT(); // we don't support that either yet
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -33,28 +29,26 @@ int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
|||
return ret > 0;
|
||||
};
|
||||
|
||||
char buf[128];
|
||||
pthread_getname_np(pthread_self(), buf, 128);
|
||||
fmt::print("Thread {} waiting for events (micros = {})\n", buf, micros);
|
||||
|
||||
if (micros == 0) {
|
||||
m_cond.wait(lock, predicate);
|
||||
} else {
|
||||
m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate);
|
||||
}
|
||||
fmt::print("Wait done\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (m_events.size() > 1) {
|
||||
BREAKPOINT(); // we currently support one event
|
||||
}
|
||||
ASSERT(m_events.size() <= 1);
|
||||
|
||||
auto& event = m_events[0];
|
||||
|
||||
if (event.filter.trigger_event_func != nullptr) {
|
||||
event.filter.trigger_event_func(&event, trigger_data);
|
||||
} else {
|
||||
event.isTriggered = true;
|
||||
}
|
||||
|
||||
event.trigger(trigger_data);
|
||||
m_cond.notify_one();
|
||||
|
||||
return true;
|
||||
|
@ -63,17 +57,12 @@ bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
|||
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||
int ret = 0;
|
||||
|
||||
if (m_events.size() > 1) {
|
||||
BREAKPOINT(); // we currently support one event
|
||||
}
|
||||
ASSERT(m_events.size() <= 1);
|
||||
auto& event = m_events[0];
|
||||
|
||||
if (event.isTriggered) {
|
||||
ev[ret++] = event.event;
|
||||
|
||||
if (event.filter.reset_event_func != nullptr) {
|
||||
event.filter.reset_event_func(&event);
|
||||
}
|
||||
event.reset();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -52,15 +52,24 @@ struct SceKernelEvent {
|
|||
|
||||
struct Filter {
|
||||
void* data = nullptr;
|
||||
TriggerFunc trigger_event_func = nullptr;
|
||||
ResetFunc reset_event_func = nullptr;
|
||||
DeleteFunc delete_event_func = nullptr;
|
||||
};
|
||||
|
||||
struct EqueueEvent {
|
||||
bool isTriggered = false;
|
||||
SceKernelEvent event;
|
||||
Filter filter;
|
||||
|
||||
void reset() {
|
||||
isTriggered = false;
|
||||
event.fflags = 0;
|
||||
event.data = 0;
|
||||
}
|
||||
|
||||
void trigger(void* data) {
|
||||
isTriggered = true;
|
||||
event.fflags++;
|
||||
event.data = reinterpret_cast<uintptr_t>(data);
|
||||
}
|
||||
};
|
||||
|
||||
class EqueueInternal {
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/PS4/GPU/gpu_memory.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/libraries/kernel/physical_memory.h"
|
||||
|
@ -13,10 +13,6 @@
|
|||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
bool is16KBAligned(u64 n) {
|
||||
return ((n % (16ull * 1024) == 0));
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() {
|
||||
LOG_WARNING(Kernel_Vmm, "called");
|
||||
return SCE_KERNEL_MAIN_DMEM_SIZE;
|
||||
|
@ -34,11 +30,11 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
|
|||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
const bool is_in_range = (searchStart < len && searchEnd > len);
|
||||
if (len <= 0 || !is16KBAligned(len) || !is_in_range) {
|
||||
if (len <= 0 || !Common::is16KBAligned(len) || !is_in_range) {
|
||||
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if ((alignment != 0 || is16KBAligned(alignment)) && !std::has_single_bit(alignment)) {
|
||||
if ((alignment != 0 || Common::is16KBAligned(alignment)) && !std::has_single_bit(alignment)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
@ -66,23 +62,22 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
|||
"len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, alignment = {:#x}",
|
||||
len, prot, flags, directMemoryStart, alignment);
|
||||
|
||||
if (len == 0 || !is16KBAligned(len)) {
|
||||
if (len == 0 || !Common::is16KBAligned(len)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (!is16KBAligned(directMemoryStart)) {
|
||||
if (!Common::is16KBAligned(directMemoryStart)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (alignment != 0) {
|
||||
if ((!std::has_single_bit(alignment) && !is16KBAligned(alignment))) {
|
||||
if ((!std::has_single_bit(alignment) && !Common::is16KBAligned(alignment))) {
|
||||
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualMemory::MemoryMode cpu_mode = VirtualMemory::MemoryMode::NoAccess;
|
||||
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
|
||||
|
||||
switch (prot) {
|
||||
case 0x03:
|
||||
|
@ -91,7 +86,6 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
|||
case 0x32:
|
||||
case 0x33: // SCE_KERNEL_PROT_CPU_READ|SCE_KERNEL_PROT_CPU_WRITE|SCE_KERNEL_PROT_GPU_READ|SCE_KERNEL_PROT_GPU_ALL
|
||||
cpu_mode = VirtualMemory::MemoryMode::ReadWrite;
|
||||
gpu_mode = GPU::MemoryMode::ReadWrite;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -112,13 +106,10 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
|||
}
|
||||
|
||||
auto* physical_memory = Common::Singleton<PhysicalMemory>::Instance();
|
||||
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode, gpu_mode)) {
|
||||
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (gpu_mode != GPU::MemoryMode::NoAccess) {
|
||||
GPU::memorySetAllocArea(out_addr, len);
|
||||
}
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "core/libraries/kernel/physical_memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static u64 AlignUp(u64 pos, u64 align) {
|
||||
return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos);
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, u64* physAddrOut,
|
||||
int memoryType) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
u64 find_free_pos = 0;
|
||||
|
||||
// iterate through allocated blocked and find the next free position
|
||||
// Iterate through allocated blocked and find the next free position
|
||||
for (const auto& block : m_allocatedBlocks) {
|
||||
u64 n = block.start_addr + block.size;
|
||||
if (n > find_free_pos) {
|
||||
|
@ -22,16 +19,15 @@ bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignmen
|
|||
}
|
||||
}
|
||||
|
||||
// align free position
|
||||
find_free_pos = AlignUp(find_free_pos, alignment);
|
||||
// Align free position
|
||||
find_free_pos = Common::alignUp(find_free_pos, alignment);
|
||||
|
||||
// if the new position is between searchStart - searchEnd , allocate a new block
|
||||
// If the new position is between searchStart - searchEnd , allocate a new block
|
||||
if (find_free_pos >= searchStart && find_free_pos + len <= searchEnd) {
|
||||
AllocatedBlock block{};
|
||||
block.size = len;
|
||||
block.start_addr = find_free_pos;
|
||||
block.memoryType = memoryType;
|
||||
block.gpu_mode = GPU::MemoryMode::NoAccess;
|
||||
block.map_size = 0;
|
||||
block.map_virtual_addr = 0;
|
||||
block.prot = 0;
|
||||
|
@ -47,7 +43,7 @@ bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignmen
|
|||
}
|
||||
|
||||
bool PhysicalMemory::Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot,
|
||||
VirtualMemory::MemoryMode cpu_mode, GPU::MemoryMode gpu_mode) {
|
||||
VirtualMemory::MemoryMode cpu_mode) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto& b : m_allocatedBlocks) {
|
||||
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size) {
|
||||
|
@ -59,7 +55,6 @@ bool PhysicalMemory::Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot,
|
|||
b.map_size = len;
|
||||
b.prot = prot;
|
||||
b.cpu_mode = cpu_mode;
|
||||
b.gpu_mode = gpu_mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
#include "core/PS4/GPU/gpu_memory.h"
|
||||
#include "core/virtual_memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
@ -21,7 +20,6 @@ public:
|
|||
u64 map_size;
|
||||
int prot;
|
||||
VirtualMemory::MemoryMode cpu_mode;
|
||||
GPU::MemoryMode gpu_mode;
|
||||
};
|
||||
PhysicalMemory() {}
|
||||
virtual ~PhysicalMemory() {}
|
||||
|
@ -29,8 +27,8 @@ public:
|
|||
public:
|
||||
bool Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, u64* physAddrOut,
|
||||
int memoryType);
|
||||
bool Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot, VirtualMemory::MemoryMode cpu_mode,
|
||||
GPU::MemoryMode gpu_mode);
|
||||
bool Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot,
|
||||
VirtualMemory::MemoryMode cpu_mode);
|
||||
|
||||
private:
|
||||
std::vector<AllocatedBlock> m_allocatedBlocks;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "core/libraries/libc/libc_string.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
constexpr bool log_file_libc = true; // disable it to disable logging
|
||||
static u32 g_need_sceLibc = 1;
|
||||
|
@ -421,7 +421,7 @@ const PS4_SYSV_ABI u16* ps4__Getpctype() {
|
|||
return &characterTypeTable[0];
|
||||
}
|
||||
|
||||
void libcSymbolsRegister(Loader::SymbolsResolver* sym) {
|
||||
void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
// cxa functions
|
||||
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, ps4___cxa_guard_acquire);
|
||||
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, ps4___cxa_guard_release);
|
||||
|
@ -490,4 +490,4 @@ void libcSymbolsRegister(Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("sUP1hBaouOw", "libc", 1, "libc", 1, 1, ps4__Getpctype);
|
||||
}
|
||||
|
||||
}; // namespace Core::Libraries::LibC
|
||||
}; // namespace Libraries::LibC
|
||||
|
|
|
@ -7,8 +7,8 @@ namespace Core::Loader {
|
|||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
void libcSymbolsRegister(Loader::SymbolsResolver* sym);
|
||||
void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// adapted from
|
||||
// https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
// This file implements the __cxa_guard_* functions as defined at:
|
||||
// http://www.codesourcery.com/public/cxx-abi/abi.html
|
||||
|
@ -158,4 +158,4 @@ void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object) {
|
|||
setNotInUse(guard_object);
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#include <pthread.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
int PS4_SYSV_ABI ps4___cxa_guard_acquire(u64* guard_object);
|
||||
void PS4_SYSV_ABI ps4___cxa_guard_release(u64* guard_object);
|
||||
void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object);
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <cmath>
|
||||
#include "core/libraries/libc/libc_math.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
float PS4_SYSV_ABI ps4_atan2f(float y, float x) {
|
||||
return atan2f(y, x);
|
||||
|
@ -38,4 +38,4 @@ double PS4_SYSV_ABI ps4_exp2(double arg) {
|
|||
return exp2(arg);
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
float PS4_SYSV_ABI ps4_atan2f(float y, float x);
|
||||
float PS4_SYSV_ABI ps4_acosf(float num);
|
||||
|
@ -16,4 +16,4 @@ double PS4_SYSV_ABI ps4__Sin(double x);
|
|||
float PS4_SYSV_ABI ps4__Fsin(float arg);
|
||||
double PS4_SYSV_ABI ps4_exp2(double arg);
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/libc/libc_stdio.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode) {
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
@ -70,4 +70,4 @@ int PS4_SYSV_ABI ps4_puts(const char* s) {
|
|||
return std::puts(s);
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "common/types.h"
|
||||
#include "core/libraries/libc/printf.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode);
|
||||
int PS4_SYSV_ABI ps4_printf(VA_ARGS);
|
||||
|
@ -19,4 +19,4 @@ int PS4_SYSV_ABI ps4_fseek(FILE* stream, long offset, int whence);
|
|||
int PS4_SYSV_ABI ps4_fgetpos(FILE* stream, fpos_t* pos);
|
||||
std::size_t PS4_SYSV_ABI ps4_fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "core/libraries/libc/libc_stdlib.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
void PS4_SYSV_ABI ps4_exit(int code) {
|
||||
std::exit(code);
|
||||
|
@ -42,4 +42,4 @@ int PS4_SYSV_ABI ps4_rand() {
|
|||
return std::rand();
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <cstddef>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
void PS4_SYSV_ABI ps4_exit(int code);
|
||||
int PS4_SYSV_ABI ps4_atexit(void (*func)());
|
||||
|
@ -16,4 +16,4 @@ void PS4_SYSV_ABI ps4_qsort(void* ptr, size_t count, size_t size,
|
|||
int(PS4_SYSV_ABI* comp)(const void*, const void*));
|
||||
int PS4_SYSV_ABI ps4_rand();
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <cstring>
|
||||
#include "core/libraries/libc/libc_string.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n) {
|
||||
return std::memcmp(s1, s2, n);
|
||||
|
@ -58,4 +58,4 @@ char* PS4_SYSV_ABI ps4_strdup(const char* str1) {
|
|||
return dup;
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <cstddef>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n);
|
||||
void* PS4_SYSV_ABI ps4_memcpy(void* dest, const void* src, size_t n);
|
||||
|
@ -21,4 +21,4 @@ char* PS4_SYSV_ABI ps4_strrchr(const char* s, int c);
|
|||
int PS4_SYSV_ABI ps4_strncmp(const char* s1, const char* s2, size_t n);
|
||||
char* PS4_SYSV_ABI ps4_strdup(const char* str1);
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
#include "va_ctx.h"
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
// ntoa conversion buffer size, this must be big enough to hold
|
||||
// one converted numeric number including padded zeros (dynamically created on stack)
|
||||
// 32 byte is a good default
|
||||
|
@ -750,4 +750,4 @@ static int vsnprintf_ctx(char* s, size_t n, const char* format, VaList* arg) {
|
|||
std::strcpy(s, buffer);
|
||||
return result;
|
||||
}
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
(ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \
|
||||
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
|
||||
|
||||
namespace Core::Libraries::LibC {
|
||||
namespace Libraries::LibC {
|
||||
|
||||
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
|
||||
|
||||
|
@ -107,4 +107,4 @@ T* vaArgPtr(VaList* l) {
|
|||
return vaArgOverflowArgArea<T*, 1, 8>(l);
|
||||
}
|
||||
|
||||
} // namespace Core::Libraries::LibC
|
||||
} // namespace Libraries::LibC
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "core/PS4/HLE/Graphics/video_out.h"
|
||||
#include "core/libraries/audio/audioin.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||
|
@ -22,16 +21,17 @@
|
|||
#include "core/libraries/system/sysmodule.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
|
||||
namespace Libraries {
|
||||
|
||||
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::Kernel::LibKernel_Register(sym);
|
||||
HLE::Libs::Graphics::VideoOut::videoOutRegisterLib(sym);
|
||||
Libraries::VideoOut::RegisterLib(sym);
|
||||
Libraries::GnmDriver::RegisterlibSceGnmDriver(sym);
|
||||
Libraries::LibPad::padSymbolsRegister(sym);
|
||||
if (!Config::isLleLibc()) {
|
||||
Core::Libraries::LibC::libcSymbolsRegister(sym);
|
||||
Libraries::LibC::libcSymbolsRegister(sym);
|
||||
}
|
||||
|
||||
// New libraries folder from autogen
|
||||
|
|
74
src/core/libraries/videoout/buffer.h
Normal file
74
src/core/libraries/videoout/buffer.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
constexpr std::size_t MaxDisplayBuffers = 16;
|
||||
constexpr std::size_t MaxDisplayBufferGroups = 4;
|
||||
|
||||
enum class PixelFormat : u32 {
|
||||
Unknown,
|
||||
A8R8G8B8Srgb = 0x80000000,
|
||||
A8B8G8R8Srgb = 0x80002200,
|
||||
A2R10G10B10 = 0x88060000,
|
||||
A2R10G10B10Srgb = 0x88000000,
|
||||
A2R10G10B10Bt2020Pq = 0x88740000,
|
||||
A16R16G16B16Float = 0xC1060000,
|
||||
};
|
||||
|
||||
enum class TilingMode : s32 {
|
||||
Tile = 0,
|
||||
Linear = 1,
|
||||
};
|
||||
|
||||
constexpr std::string_view GetPixelFormatString(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8R8G8B8Srgb:
|
||||
return "A8R8G8B8Srgb";
|
||||
case PixelFormat::A8B8G8R8Srgb:
|
||||
return "A8B8G8R8Srgb";
|
||||
case PixelFormat::A2R10G10B10:
|
||||
return "A2R10G10B10";
|
||||
case PixelFormat::A2R10G10B10Srgb:
|
||||
return "A2R10G10B10Srgb";
|
||||
case PixelFormat::A2R10G10B10Bt2020Pq:
|
||||
return "A2R10G10B10Bt2020Pq";
|
||||
case PixelFormat::A16R16G16B16Float:
|
||||
return "A16R16G16B16Float";
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown pixel format {}", static_cast<u32>(format));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
struct BufferAttribute {
|
||||
PixelFormat pixel_format;
|
||||
TilingMode tiling_mode;
|
||||
s32 aspect_ratio;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 pitch_in_pixel;
|
||||
u32 option;
|
||||
u32 reserved0;
|
||||
u64 reserved1;
|
||||
};
|
||||
|
||||
struct BufferAttributeGroup {
|
||||
bool is_occupied;
|
||||
BufferAttribute attrib;
|
||||
u32 size_in_bytes;
|
||||
};
|
||||
|
||||
struct VideoOutBuffer {
|
||||
s32 group_index{-1};
|
||||
uintptr_t address_left;
|
||||
uintptr_t address_right;
|
||||
};
|
||||
|
||||
} // namespace Libraries::VideoOut
|
236
src/core/libraries/videoout/driver.cpp
Normal file
236
src/core/libraries/videoout/driver.cpp
Normal file
|
@ -0,0 +1,236 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <pthread.h>
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
extern Frontend::WindowSDL* g_window;
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
constexpr static bool Is32BppPixelFormat(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8R8G8B8Srgb:
|
||||
case PixelFormat::A8B8G8R8Srgb:
|
||||
case PixelFormat::A2R10G10B10:
|
||||
case PixelFormat::A2R10G10B10Srgb:
|
||||
case PixelFormat::A2R10G10B10Bt2020Pq:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 PixelFormatBpp(PixelFormat pixel_format) {
|
||||
switch (pixel_format) {
|
||||
case PixelFormat::A16R16G16B16Float:
|
||||
return 8;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
VideoOutDriver::VideoOutDriver(u32 width, u32 height) {
|
||||
main_port.resolution.fullWidth = width;
|
||||
main_port.resolution.fullHeight = height;
|
||||
main_port.resolution.paneWidth = width;
|
||||
main_port.resolution.paneHeight = height;
|
||||
|
||||
renderer = std::make_unique<Vulkan::RendererVulkan>(*g_window);
|
||||
}
|
||||
|
||||
VideoOutDriver::~VideoOutDriver() = default;
|
||||
|
||||
int VideoOutDriver::Open(const ServiceThreadParams* params) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (main_port.is_open) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
int handle = 1;
|
||||
main_port.is_open = true;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Close(s32 handle) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
main_port.is_open = false;
|
||||
main_port.flip_rate = 0;
|
||||
ASSERT(main_port.flip_events.empty());
|
||||
}
|
||||
|
||||
VideoOutPort* VideoOutDriver::GetPort(int handle) {
|
||||
if (handle != 1) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
return &main_port;
|
||||
}
|
||||
|
||||
int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* const* addresses,
|
||||
s32 bufferNum, const BufferAttribute* attribute) {
|
||||
const s32 group_index = port->FindFreeGroup();
|
||||
if (group_index >= MaxDisplayBufferGroups) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_NO_EMPTY_SLOT;
|
||||
}
|
||||
|
||||
if (startIndex + bufferNum > MaxDisplayBuffers || startIndex > MaxDisplayBuffers ||
|
||||
bufferNum > MaxDisplayBuffers) {
|
||||
LOG_ERROR(Lib_VideoOut,
|
||||
"Attempted to register too many buffers startIndex = {}, bufferNum = {}",
|
||||
startIndex, bufferNum);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
const s32 end_index = startIndex + bufferNum;
|
||||
if (bufferNum > 0 &&
|
||||
std::any_of(port->buffer_slots.begin() + startIndex, port->buffer_slots.begin() + end_index,
|
||||
[](auto& buffer) { return buffer.group_index != -1; })) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_SLOT_OCCUPIED;
|
||||
}
|
||||
|
||||
if (attribute->reserved0 != 0 || attribute->reserved1 != 0) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid reserved memebers");
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (attribute->aspect_ratio != 0) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid aspect ratio = {}", attribute->aspect_ratio);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO;
|
||||
}
|
||||
if (attribute->width > attribute->pitch_in_pixel) {
|
||||
LOG_ERROR(Lib_VideoOut, "Buffer width {} is larger than pitch {}", attribute->width,
|
||||
attribute->pitch_in_pixel);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_PITCH;
|
||||
}
|
||||
if (attribute->tiling_mode < TilingMode::Tile || attribute->tiling_mode > TilingMode::Linear) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid tilingMode = {}",
|
||||
static_cast<u32>(attribute->tiling_mode));
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_TILING_MODE;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_VideoOut,
|
||||
"startIndex = {}, bufferNum = {}, pixelFormat = {}, aspectRatio = {}, "
|
||||
"tilingMode = {}, width = {}, height = {}, pitchInPixel = {}, option = {:#x}",
|
||||
startIndex, bufferNum, GetPixelFormatString(attribute->pixel_format),
|
||||
attribute->aspect_ratio, static_cast<u32>(attribute->tiling_mode), attribute->width,
|
||||
attribute->height, attribute->pitch_in_pixel, attribute->option);
|
||||
|
||||
auto& group = port->groups[group_index];
|
||||
std::memcpy(&group.attrib, attribute, sizeof(BufferAttribute));
|
||||
group.size_in_bytes =
|
||||
attribute->height * attribute->pitch_in_pixel * PixelFormatBpp(attribute->pixel_format);
|
||||
group.is_occupied = true;
|
||||
|
||||
for (u32 i = 0; i < bufferNum; i++) {
|
||||
const uintptr_t address = reinterpret_cast<uintptr_t>(addresses[i]);
|
||||
port->buffer_slots[startIndex + i] = VideoOutBuffer{
|
||||
.group_index = group_index,
|
||||
.address_left = address,
|
||||
.address_right = 0,
|
||||
};
|
||||
|
||||
LOG_INFO(Lib_VideoOut, "buffers[{}] = {:#x}", i + startIndex, address);
|
||||
}
|
||||
|
||||
return group_index;
|
||||
}
|
||||
|
||||
int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
|
||||
if (attributeIndex >= MaxDisplayBufferGroups || !port->groups[attributeIndex].is_occupied) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid attribute index {}", attributeIndex);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
auto& group = port->groups[attributeIndex];
|
||||
group.is_occupied = false;
|
||||
|
||||
for (auto& buffer : port->buffer_slots) {
|
||||
if (buffer.group_index != attributeIndex) {
|
||||
continue;
|
||||
}
|
||||
buffer.group_index = -1;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
||||
Request req;
|
||||
{
|
||||
std::unique_lock lock{mutex};
|
||||
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
|
||||
if (requests.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the request.
|
||||
req = requests.front();
|
||||
requests.pop();
|
||||
}
|
||||
|
||||
// Present the frame.
|
||||
renderer->Present(req.frame);
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
// Update flip status.
|
||||
auto& flip_status = req.port->flip_status;
|
||||
flip_status.count++;
|
||||
flip_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
flip_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
flip_status.submitTsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
flip_status.flipArg = req.flip_arg;
|
||||
flip_status.currentBuffer = req.index;
|
||||
flip_status.flipPendingNum = static_cast<int>(requests.size());
|
||||
|
||||
// Trigger flip events for the port.
|
||||
for (auto& event : req.port->flip_events) {
|
||||
if (event != nullptr) {
|
||||
event->triggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::EVFILT_VIDEO_OUT,
|
||||
reinterpret_cast<void*>(req.flip_arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg) {
|
||||
const auto& buffer = port->buffer_slots[index];
|
||||
const auto& group = port->groups[buffer.group_index];
|
||||
auto* frame = renderer->PrepareFrame(group, buffer.address_left);
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (requests.size() >= 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
requests.push({
|
||||
.frame = frame,
|
||||
.port = port,
|
||||
.index = index,
|
||||
.flip_arg = flip_arg,
|
||||
.submit_tsc = Libraries::Kernel::sceKernelReadTsc(),
|
||||
});
|
||||
|
||||
port->flip_status.flipPendingNum = static_cast<int>(requests.size());
|
||||
port->flip_status.gcQueueNum = 0;
|
||||
submit_cond.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Vblank() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
auto& vblank_status = main_port.vblank_status;
|
||||
vblank_status.count++;
|
||||
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
}
|
||||
|
||||
} // namespace Libraries::VideoOut
|
82
src/core/libraries/videoout/driver.h
Normal file
82
src/core/libraries/videoout/driver.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
|
||||
namespace Vulkan {
|
||||
struct Frame;
|
||||
class RendererVulkan;
|
||||
} // namespace Vulkan
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
struct VideoOutPort {
|
||||
bool is_open = false;
|
||||
SceVideoOutResolutionStatus resolution;
|
||||
std::array<VideoOutBuffer, MaxDisplayBuffers> buffer_slots;
|
||||
std::array<BufferAttributeGroup, MaxDisplayBufferGroups> groups;
|
||||
FlipStatus flip_status;
|
||||
SceVideoOutVblankStatus vblank_status;
|
||||
std::vector<Kernel::SceKernelEqueue> flip_events;
|
||||
int flip_rate = 0;
|
||||
|
||||
s32 FindFreeGroup() const {
|
||||
s32 index = 0;
|
||||
while (index < groups.size() && groups[index].is_occupied) {
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
struct ServiceThreadParams {
|
||||
u32 unknown;
|
||||
bool set_priority;
|
||||
u32 priority;
|
||||
bool set_affinity;
|
||||
u64 affinity;
|
||||
};
|
||||
|
||||
class VideoOutDriver {
|
||||
public:
|
||||
explicit VideoOutDriver(u32 width, u32 height);
|
||||
~VideoOutDriver();
|
||||
|
||||
int Open(const ServiceThreadParams* params);
|
||||
void Close(s32 handle);
|
||||
|
||||
VideoOutPort* GetPort(s32 handle);
|
||||
|
||||
int RegisterBuffers(VideoOutPort* port, s32 startIndex, void* const* addresses, s32 bufferNum,
|
||||
const BufferAttribute* attribute);
|
||||
int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex);
|
||||
|
||||
void Flip(std::chrono::microseconds timeout);
|
||||
bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg);
|
||||
|
||||
void Vblank();
|
||||
|
||||
private:
|
||||
struct Request {
|
||||
Vulkan::Frame* frame;
|
||||
VideoOutPort* port;
|
||||
s32 index;
|
||||
s64 flip_arg;
|
||||
u64 submit_tsc;
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
VideoOutPort main_port{};
|
||||
std::condition_variable_any submit_cond;
|
||||
std::condition_variable done_cond;
|
||||
std::queue<Request> requests;
|
||||
std::unique_ptr<Vulkan::RendererVulkan> renderer;
|
||||
bool is_neo{};
|
||||
};
|
||||
|
||||
} // namespace Libraries::VideoOut
|
236
src/core/libraries/videoout/video_out.cpp
Normal file
236
src/core/libraries/videoout/video_out.cpp
Normal file
|
@ -0,0 +1,236 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
static std::unique_ptr<VideoOutDriver> driver;
|
||||
|
||||
void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat,
|
||||
u32 tilingMode, u32 aspectRatio, u32 width,
|
||||
u32 height, u32 pitchInPixel) {
|
||||
LOG_INFO(Lib_VideoOut,
|
||||
"pixelFormat = {}, tilingMode = {}, aspectRatio = {}, width = {}, height = {}, "
|
||||
"pitchInPixel = {}",
|
||||
GetPixelFormatString(pixelFormat), tilingMode, aspectRatio, width, height,
|
||||
pitchInPixel);
|
||||
|
||||
std::memset(attribute, 0, sizeof(BufferAttribute));
|
||||
attribute->pixel_format = static_cast<PixelFormat>(pixelFormat);
|
||||
attribute->tiling_mode = static_cast<TilingMode>(tilingMode);
|
||||
attribute->aspect_ratio = aspectRatio;
|
||||
attribute->width = width;
|
||||
attribute->height = height;
|
||||
attribute->pitch_in_pixel = pitchInPixel;
|
||||
attribute->option = SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||
LOG_INFO(Lib_VideoOut, "handle = {}", handle);
|
||||
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (port == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (eq == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
|
||||
Kernel::EqueueEvent event{};
|
||||
event.isTriggered = false;
|
||||
event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP;
|
||||
event.event.filter = Kernel::EVFILT_VIDEO_OUT;
|
||||
event.event.udata = udata;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.filter.data = port;
|
||||
|
||||
port->flip_events.push_back(eq);
|
||||
return eq->addEvent(event);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,
|
||||
s32 bufferNum, const BufferAttribute* attribute) {
|
||||
if (!addresses || !attribute) {
|
||||
LOG_ERROR(Lib_VideoOut, "Addresses are null");
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port || !port->is_open) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return driver->RegisterBuffers(port, startIndex, addresses, bufferNum, attribute);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
||||
LOG_INFO(Lib_VideoOut, "called");
|
||||
driver->GetPort(handle)->flip_rate = rate;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
||||
LOG_INFO(Lib_VideoOut, "called");
|
||||
s32 pending = driver->GetPort(handle)->flip_status.flipPendingNum;
|
||||
return pending;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (flipMode != 1) {
|
||||
LOG_WARNING(Lib_VideoOut, "flipmode = {}", flipMode);
|
||||
}
|
||||
|
||||
ASSERT_MSG(bufferIndex != -1, "Blank output not supported");
|
||||
|
||||
if (bufferIndex < -1 || bufferIndex > 15) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid bufferIndex = {}", bufferIndex);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX;
|
||||
}
|
||||
|
||||
if (port->buffer_slots[bufferIndex].group_index < 0) {
|
||||
LOG_ERROR(Lib_VideoOut, "Slot in bufferIndex = {} is not registered", bufferIndex);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_VideoOut, "bufferIndex = {}, flipMode = {}, flipArg = {}", bufferIndex, flipMode,
|
||||
flipArg);
|
||||
|
||||
if (!driver->SubmitFlip(port, bufferIndex, flipArg)) {
|
||||
LOG_ERROR(Lib_VideoOut, "Flip queue is full");
|
||||
return ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) {
|
||||
if (!status) {
|
||||
LOG_ERROR(Lib_VideoOut, "Flip status is null");
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port) {
|
||||
LOG_ERROR(Lib_VideoOut, "Invalid port handle = {}", handle);
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
*status = port->flip_status;
|
||||
|
||||
LOG_INFO(Lib_VideoOut,
|
||||
"count = {}, processTime = {}, tsc = {}, submitTsc = {}, flipArg = {}, gcQueueNum = "
|
||||
"{}, flipPendingNum = {}, currentBuffer = {}",
|
||||
status->count, status->processTime, status->tsc, status->submitTsc, status->flipArg,
|
||||
status->gcQueueNum, status->flipPendingNum, status->currentBuffer);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
|
||||
LOG_INFO(Lib_VideoOut, "called");
|
||||
*status = driver->GetPort(handle)->resolution;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index,
|
||||
const void* param) {
|
||||
LOG_INFO(Lib_VideoOut, "called");
|
||||
ASSERT(userId == UserService::ORBIS_USER_SERVICE_USER_ID_SYSTEM || userId == 0);
|
||||
ASSERT(busType == SCE_VIDEO_OUT_BUS_TYPE_MAIN);
|
||||
ASSERT(param == nullptr);
|
||||
|
||||
if (index != 0) {
|
||||
LOG_ERROR(Lib_VideoOut, "Index != 0");
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
auto* params = reinterpret_cast<const ServiceThreadParams*>(param);
|
||||
int handle = driver->Open(params);
|
||||
|
||||
if (handle < 0) {
|
||||
LOG_ERROR(Lib_VideoOut, "All available handles are open");
|
||||
return ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle) {
|
||||
driver->Close(handle);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port || !port->is_open) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return driver->UnregisterBuffers(port, attributeIndex);
|
||||
}
|
||||
|
||||
void Flip(std::chrono::microseconds micros) {
|
||||
return driver->Flip(micros);
|
||||
}
|
||||
|
||||
void Vblank() {
|
||||
return driver->Vblank();
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
driver = std::make_unique<VideoOutDriver>(Config::getScreenWidth(), Config::getScreenHeight());
|
||||
|
||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutGetFlipStatus);
|
||||
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutSubmitFlip);
|
||||
LIB_FUNCTION("w3BY+tAEiQY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutRegisterBuffers);
|
||||
LIB_FUNCTION("HXzjK9yI30k", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutAddFlipEvent);
|
||||
LIB_FUNCTION("CBiu4mCE1DA", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutSetFlipRate);
|
||||
LIB_FUNCTION("i6-sR91Wt-4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutSetBufferAttribute);
|
||||
LIB_FUNCTION("6kPnj51T62Y", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutGetResolutionStatus);
|
||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutOpen);
|
||||
LIB_FUNCTION("zgXifHT9ErY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutIsFlipPending);
|
||||
LIB_FUNCTION("N5KDtkIjjJ4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutUnregisterBuffers);
|
||||
LIB_FUNCTION("uquVH4-Du78", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutClose);
|
||||
|
||||
// openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
|
||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
|
||||
LIB_FUNCTION("CBiu4mCE1DA", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
|
||||
sceVideoOutSetFlipRate);
|
||||
LIB_FUNCTION("HXzjK9yI30k", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
|
||||
sceVideoOutAddFlipEvent);
|
||||
LIB_FUNCTION("i6-sR91Wt-4", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
|
||||
sceVideoOutSetBufferAttribute);
|
||||
LIB_FUNCTION("w3BY+tAEiQY", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
|
||||
sceVideoOutRegisterBuffers);
|
||||
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip);
|
||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
|
||||
sceVideoOutGetFlipStatus);
|
||||
}
|
||||
|
||||
} // namespace Libraries::VideoOut
|
107
src/core/libraries/videoout/video_out.h
Normal file
107
src/core/libraries/videoout/video_out.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/kernel/event_queues.h"
|
||||
#include "core/libraries/videoout/buffer.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
using SceUserServiceUserId = s32; // TODO move it to proper place
|
||||
|
||||
// SceVideoOutBusType
|
||||
constexpr int SCE_VIDEO_OUT_BUS_TYPE_MAIN = 0; // Main output
|
||||
constexpr int SCE_VIDEO_OUT_BUS_TYPE_AUX_SOCIAL_SCREEN = 5; // Aux output for social
|
||||
constexpr int SCE_VIDEO_OUT_BUS_TYPE_AUX_GAME_LIVE_STREAMING = 6; // Aux output for screaming
|
||||
|
||||
// SceVideoOutRefreshRate
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_UNKNOWN = 0;
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_23_98HZ = 1;
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_50HZ = 2;
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ = 3;
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_119_88HZ = 13;
|
||||
constexpr int SCE_VIDEO_OUT_REFRESH_RATE_89_91HZ = 35;
|
||||
constexpr s64 SCE_VIDEO_OUT_REFRESH_RATE_ANY = 0xFFFFFFFFFFFFFFFFUL;
|
||||
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A8R8G8B8_SRGB = 0x80000000;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A8B8G8R8_SRGB = 0x80002200;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10 = 0x88060000;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_SRGB = 0x88000000;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_BT2020_PQ = 0x88740000;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_A16R16G16B16_FLOAT = 0xC1060000;
|
||||
constexpr int SCE_VIDEO_OUT_PIXEL_FORMAT_YCBCR420_BT709 = 0x08322200;
|
||||
|
||||
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE = 0;
|
||||
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_VR = 7;
|
||||
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8;
|
||||
|
||||
enum SceVideoOutEventId : s16 {
|
||||
SCE_VIDEO_OUT_EVENT_FLIP = 0,
|
||||
SCE_VIDEO_OUT_EVENT_VBLANK = 1,
|
||||
SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START = 2
|
||||
};
|
||||
|
||||
enum AspectRatioMode : s32 {
|
||||
SCE_VIDEO_OUT_ASPECT_RATIO_16_9 = 0,
|
||||
};
|
||||
|
||||
struct FlipStatus {
|
||||
u64 count = 0;
|
||||
u64 processTime = 0;
|
||||
u64 tsc = 0;
|
||||
s64 flipArg = -1;
|
||||
u64 submitTsc = 0;
|
||||
u64 reserved0 = 0;
|
||||
s32 gcQueueNum = 0;
|
||||
s32 flipPendingNum = 0;
|
||||
s32 currentBuffer = -1;
|
||||
u32 reserved1 = 0;
|
||||
};
|
||||
|
||||
struct SceVideoOutResolutionStatus {
|
||||
s32 fullWidth = 1280;
|
||||
s32 fullHeight = 720;
|
||||
s32 paneWidth = 1280;
|
||||
s32 paneHeight = 720;
|
||||
u64 refreshRate = SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ;
|
||||
float screenSizeInInch = 50;
|
||||
u16 flags = 0;
|
||||
u16 reserved0 = 0;
|
||||
u32 reserved1[3] = {0};
|
||||
};
|
||||
|
||||
struct SceVideoOutVblankStatus {
|
||||
u64 count = 0;
|
||||
u64 processTime = 0;
|
||||
u64 tsc = 0;
|
||||
u64 reserved[1] = {0};
|
||||
u8 flags = 0;
|
||||
u8 pad1[7] = {};
|
||||
};
|
||||
|
||||
void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat,
|
||||
u32 tilingMode, u32 aspectRatio, u32 width,
|
||||
u32 height, u32 pitchInPixel);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
|
||||
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,
|
||||
s32 bufferNum, const BufferAttribute* attribute);
|
||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate);
|
||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index,
|
||||
const void* param);
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||
|
||||
void Flip(std::chrono::microseconds micros);
|
||||
void Vblank();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::VideoOut
|
Loading…
Add table
Add a link
Reference in a new issue