mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-02 19:08:03 -06:00
Merge branch 'shadps4-emu:main' into ime-fixes-again
This commit is contained in:
commit
bd3942e8ad
4
.github/linux-appimage-sdl.sh
vendored
4
.github/linux-appimage-sdl.sh
vendored
@ -8,8 +8,8 @@ if [[ -z $GITHUB_WORKSPACE ]]; then
|
||||
fi
|
||||
|
||||
# Prepare Tools for building the AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
|
||||
wget --waitretry=3 --read-timeout=20 --timeout=15 --tries=5 -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget --waitretry=3 --read-timeout=20 --timeout=15 --tries=5 -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
|
||||
|
||||
chmod a+x linuxdeploy-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
|
||||
|
||||
@ -174,7 +174,7 @@ static ConfigEntry<u32> internalScreenWidth(1280);
|
||||
static ConfigEntry<u32> internalScreenHeight(720);
|
||||
static ConfigEntry<bool> isNullGpu(false);
|
||||
static ConfigEntry<bool> shouldCopyGPUBuffers(false);
|
||||
static ConfigEntry<bool> readbacksEnabled(false);
|
||||
static ConfigEntry<int> readbacksMode(GpuReadbacksMode::Disabled);
|
||||
static ConfigEntry<bool> readbackLinearImagesEnabled(false);
|
||||
static ConfigEntry<bool> directMemoryAccessEnabled(false);
|
||||
static ConfigEntry<bool> shouldDumpShaders(false);
|
||||
@ -442,8 +442,8 @@ bool copyGPUCmdBuffers() {
|
||||
return shouldCopyGPUBuffers.get();
|
||||
}
|
||||
|
||||
bool readbacks() {
|
||||
return readbacksEnabled.get();
|
||||
int getReadbacksMode() {
|
||||
return readbacksMode.get();
|
||||
}
|
||||
|
||||
bool readbackLinearImages() {
|
||||
@ -593,8 +593,8 @@ void setCopyGPUCmdBuffers(bool enable, bool is_game_specific) {
|
||||
shouldCopyGPUBuffers.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setReadbacks(bool enable, bool is_game_specific) {
|
||||
readbacksEnabled.set(enable, is_game_specific);
|
||||
void setReadbacksMode(int mode, bool is_game_specific) {
|
||||
readbacksMode.set(mode, is_game_specific);
|
||||
}
|
||||
|
||||
void setReadbackLinearImages(bool enable, bool is_game_specific) {
|
||||
@ -963,7 +963,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
internalScreenHeight.setFromToml(gpu, "internalScreenHeight", is_game_specific);
|
||||
isNullGpu.setFromToml(gpu, "nullGpu", is_game_specific);
|
||||
shouldCopyGPUBuffers.setFromToml(gpu, "copyGPUBuffers", is_game_specific);
|
||||
readbacksEnabled.setFromToml(gpu, "readbacks", is_game_specific);
|
||||
readbacksMode.setFromToml(gpu, "readbacksMode", is_game_specific);
|
||||
readbackLinearImagesEnabled.setFromToml(gpu, "readbackLinearImages", is_game_specific);
|
||||
directMemoryAccessEnabled.setFromToml(gpu, "directMemoryAccess", is_game_specific);
|
||||
shouldDumpShaders.setFromToml(gpu, "dumpShaders", is_game_specific);
|
||||
@ -1142,7 +1142,7 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
|
||||
windowHeight.setTomlValue(data, "GPU", "screenHeight", is_game_specific);
|
||||
isNullGpu.setTomlValue(data, "GPU", "nullGpu", is_game_specific);
|
||||
shouldCopyGPUBuffers.setTomlValue(data, "GPU", "copyGPUBuffers", is_game_specific);
|
||||
readbacksEnabled.setTomlValue(data, "GPU", "readbacks", is_game_specific);
|
||||
readbacksMode.setTomlValue(data, "GPU", "readbacksMode", is_game_specific);
|
||||
readbackLinearImagesEnabled.setTomlValue(data, "GPU", "readbackLinearImages", is_game_specific);
|
||||
shouldDumpShaders.setTomlValue(data, "GPU", "dumpShaders", is_game_specific);
|
||||
vblankFrequency.setTomlValue(data, "GPU", "vblankFrequency", is_game_specific);
|
||||
@ -1241,7 +1241,7 @@ void setDefaultValues(bool is_game_specific) {
|
||||
// Entries with game-specific settings that are in the game-specific setings GUI but not in
|
||||
// the global settings GUI
|
||||
if (is_game_specific) {
|
||||
readbacksEnabled.set(false, is_game_specific);
|
||||
readbacksMode.set(GpuReadbacksMode::Disabled, is_game_specific);
|
||||
readbackLinearImagesEnabled.set(false, is_game_specific);
|
||||
isNeo.set(false, is_game_specific);
|
||||
isDevKit.set(false, is_game_specific);
|
||||
|
||||
@ -23,6 +23,12 @@ struct GameInstallDir {
|
||||
|
||||
enum HideCursorState : int { Never, Idle, Always };
|
||||
|
||||
enum GpuReadbacksMode : int {
|
||||
Disabled,
|
||||
Relaxed,
|
||||
Precise,
|
||||
};
|
||||
|
||||
void load(const std::filesystem::path& path, bool is_game_specific = false);
|
||||
void save(const std::filesystem::path& path, bool is_game_specific = false);
|
||||
void resetGameSpecificValue(std::string entry);
|
||||
@ -63,8 +69,8 @@ bool nullGpu();
|
||||
void setNullGpu(bool enable, bool is_game_specific = false);
|
||||
bool copyGPUCmdBuffers();
|
||||
void setCopyGPUCmdBuffers(bool enable, bool is_game_specific = false);
|
||||
bool readbacks();
|
||||
void setReadbacks(bool enable, bool is_game_specific = false);
|
||||
int getReadbacksMode();
|
||||
void setReadbacksMode(int mode, bool is_game_specific = false);
|
||||
bool readbackLinearImages();
|
||||
void setReadbackLinearImages(bool enable, bool is_game_specific = false);
|
||||
bool directMemoryAccess();
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
@ -20,12 +21,21 @@ static constexpr u32 AUDIO3D_SAMPLE_RATE = 48000;
|
||||
static constexpr AudioOut::OrbisAudioOutParamFormat AUDIO3D_OUTPUT_FORMAT =
|
||||
AudioOut::OrbisAudioOutParamFormat::S16Stereo;
|
||||
static constexpr u32 AUDIO3D_OUTPUT_NUM_CHANNELS = 2;
|
||||
static constexpr u32 AUDIO3D_OUTPUT_BUFFER_FRAMES = 0x100;
|
||||
|
||||
static std::unique_ptr<Audio3dState> state;
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) {
|
||||
LOG_INFO(Lib_Audio3d, "called, handle = {}", handle);
|
||||
|
||||
// Remove from any port that was tracking this handle.
|
||||
if (state) {
|
||||
for (auto& [port_id, port] : state->ports) {
|
||||
std::scoped_lock lock{port.mutex};
|
||||
auto& handles = port.audioout_handles;
|
||||
handles.erase(std::remove(handles.begin(), handles.end(), handle), handles.end());
|
||||
}
|
||||
}
|
||||
|
||||
return AudioOut::sceAudioOutClose(handle);
|
||||
}
|
||||
|
||||
@ -42,13 +52,21 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{state->ports[port_id].mutex};
|
||||
if (len != state->ports[port_id].parameters.granularity) {
|
||||
LOG_ERROR(Lib_Audio3d, "len != state->ports[port_id].parameters.granularity");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return sceAudioOutOpen(user_id, static_cast<AudioOut::OrbisAudioOutPort>(type), index, len,
|
||||
freq, param);
|
||||
const s32 handle = sceAudioOutOpen(user_id, static_cast<AudioOut::OrbisAudioOutPort>(type),
|
||||
index, len, freq, param);
|
||||
if (handle < 0) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Track this handle in the port so sceAudio3dPortFlush can use it for sync.
|
||||
state->ports[port_id].audioout_handles.push_back(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(const s32 handle, void* ptr) {
|
||||
@ -79,34 +97,31 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* p
|
||||
return AudioOut::sceAudioOutOutputs(param, num);
|
||||
}
|
||||
|
||||
static s32 PortQueueAudio(Port& port, const OrbisAudio3dPcm& pcm, const u32 num_channels) {
|
||||
// Audio3d output is configured for stereo signed 16-bit PCM. Convert the data to match.
|
||||
const SDL_AudioSpec src_spec = {
|
||||
.format = pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? SDL_AUDIO_S16LE
|
||||
: SDL_AUDIO_F32LE,
|
||||
.channels = static_cast<int>(num_channels),
|
||||
.freq = AUDIO3D_SAMPLE_RATE,
|
||||
};
|
||||
constexpr SDL_AudioSpec dst_spec = {
|
||||
.format = SDL_AUDIO_S16LE,
|
||||
.channels = AUDIO3D_OUTPUT_NUM_CHANNELS,
|
||||
.freq = AUDIO3D_SAMPLE_RATE,
|
||||
};
|
||||
const auto src_size = pcm.num_samples *
|
||||
(pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? 2 : 4) *
|
||||
num_channels;
|
||||
static s32 ConvertAndEnqueue(std::deque<AudioData>& queue, const OrbisAudio3dPcm& pcm,
|
||||
const u32 num_channels, const u32 granularity) {
|
||||
if (!pcm.sample_buffer || !pcm.num_samples) {
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
u8* dst_data;
|
||||
int dst_len;
|
||||
if (!SDL_ConvertAudioSamples(&src_spec, static_cast<u8*>(pcm.sample_buffer),
|
||||
static_cast<int>(src_size), &dst_spec, &dst_data, &dst_len)) {
|
||||
LOG_ERROR(Lib_Audio3d, "SDL_ConvertAudioSamples failed: {}", SDL_GetError());
|
||||
const u32 bytes_per_sample =
|
||||
(pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) ? sizeof(s16) : sizeof(float);
|
||||
|
||||
// Always allocate exactly granularity samples (zeroed = silence for padding).
|
||||
const u32 dst_bytes = granularity * num_channels * bytes_per_sample;
|
||||
u8* copy = static_cast<u8*>(std::calloc(1, dst_bytes));
|
||||
if (!copy) {
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
port.queue.emplace_back(AudioData{
|
||||
.sample_buffer = dst_data,
|
||||
.num_samples = pcm.num_samples,
|
||||
// Copy min(provided, granularity) samples — extra are dropped, shortage stays zero.
|
||||
const u32 samples_to_copy = std::min(pcm.num_samples, granularity);
|
||||
std::memcpy(copy, pcm.sample_buffer, samples_to_copy * num_channels * bytes_per_sample);
|
||||
|
||||
queue.emplace_back(AudioData{
|
||||
.sample_buffer = copy,
|
||||
.num_samples = granularity,
|
||||
.num_channels = num_channels,
|
||||
.format = pcm.format,
|
||||
});
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -145,8 +160,8 @@ s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (num_channels != 2 && num_channels != 8) {
|
||||
LOG_ERROR(Lib_Audio3d, "num_channels != 2 && num_channels != 8");
|
||||
if (num_channels != 2 && num_channels != 6 && num_channels != 8) {
|
||||
LOG_ERROR(Lib_Audio3d, "num_channels must be 2, 6, or 8");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
@ -167,13 +182,14 @@ s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32
|
||||
}
|
||||
}
|
||||
|
||||
return PortQueueAudio(state->ports[port_id],
|
||||
OrbisAudio3dPcm{
|
||||
.format = format,
|
||||
.sample_buffer = buffer,
|
||||
.num_samples = num_samples,
|
||||
},
|
||||
num_channels);
|
||||
std::scoped_lock lock{state->ports[port_id].mutex};
|
||||
return ConvertAndEnqueue(state->ports[port_id].bed_queue,
|
||||
OrbisAudio3dPcm{
|
||||
.format = format,
|
||||
.sample_buffer = buffer,
|
||||
.num_samples = num_samples,
|
||||
},
|
||||
num_channels, state->ports[port_id].parameters.granularity);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray() {
|
||||
@ -237,15 +253,6 @@ s32 PS4_SYSV_ABI sceAudio3dInitialize(const s64 reserved) {
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
AudioOut::OrbisAudioOutParamExtendedInformation ext_info{};
|
||||
ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT);
|
||||
state->audio_out_handle =
|
||||
AudioOut::sceAudioOutOpen(0xFF, AudioOut::OrbisAudioOutPort::Audio3d, 0,
|
||||
AUDIO3D_OUTPUT_BUFFER_FRAMES, AUDIO3D_SAMPLE_RATE, ext_info);
|
||||
if (state->audio_out_handle < 0) {
|
||||
return state->audio_out_handle;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -254,18 +261,84 @@ s32 PS4_SYSV_ABI sceAudio3dObjectReserve(const OrbisAudio3dPortId port_id,
|
||||
LOG_INFO(Lib_Audio3d, "called, port_id = {}, object_id = {}", port_id,
|
||||
static_cast<void*>(object_id));
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
if (!object_id) {
|
||||
LOG_ERROR(Lib_Audio3d, "!object_id");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static int last_id = 0;
|
||||
*object_id = ++last_id;
|
||||
*object_id = ORBIS_AUDIO3D_OBJECT_INVALID;
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
|
||||
// Enforce the max_objects limit set at PortOpen time.
|
||||
if (port.objects.size() >= port.parameters.max_objects) {
|
||||
LOG_ERROR(Lib_Audio3d, "port has no available objects (max_objects = {})",
|
||||
port.parameters.max_objects);
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
// counter lives in the Port so it resets when the port is closed and reopened,
|
||||
do {
|
||||
++port.next_object_id;
|
||||
} while (port.next_object_id == 0 || port.next_object_id == ORBIS_AUDIO3D_OBJECT_INVALID ||
|
||||
port.objects.contains(port.next_object_id));
|
||||
|
||||
*object_id = port.next_object_id;
|
||||
port.objects.emplace(*object_id, ObjectState{});
|
||||
LOG_INFO(Lib_Audio3d, "reserved object_id = {}", *object_id);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectSetAttribute(const OrbisAudio3dPortId port_id,
|
||||
const OrbisAudio3dObjectId object_id,
|
||||
const OrbisAudio3dAttributeId attribute_id,
|
||||
const void* attribute, const u64 attribute_size) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, object_id = {}, attribute_id = {:#x}, size = {}",
|
||||
port_id, object_id, static_cast<u32>(attribute_id), attribute_size);
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
if (!port.objects.contains(object_id)) {
|
||||
LOG_DEBUG(Lib_Audio3d, "object_id {} not reserved (race with Unreserve?), no-op",
|
||||
object_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
if (!attribute_size &&
|
||||
attribute_id != OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_RESET_STATE) {
|
||||
LOG_ERROR(Lib_Audio3d, "!attribute_size for non-reset attribute");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& obj = port.objects[object_id];
|
||||
|
||||
// RESET_STATE clears all attributes and queued PCM; it takes no value.
|
||||
if (attribute_id == OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_RESET_STATE) {
|
||||
for (auto& data : obj.pcm_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
obj.pcm_queue.clear();
|
||||
obj.persistent_attributes.clear();
|
||||
LOG_DEBUG(Lib_Audio3d, "RESET_STATE for object {}", object_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
// we don't handle any attributes yet, but store them in the ObjectState so they're available
|
||||
// when we do
|
||||
const auto* src = static_cast<const u8*>(attribute);
|
||||
obj.persistent_attributes[static_cast<u32>(attribute_id)].assign(src, src + attribute_size);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -283,32 +356,95 @@ s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(const OrbisAudio3dPortId port_id,
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
if (!num_attributes || !attribute_array) {
|
||||
LOG_ERROR(Lib_Audio3d, "!num_attributes || !attribute_array");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
if (!port.objects.contains(object_id)) {
|
||||
LOG_DEBUG(Lib_Audio3d, "object_id {} not reserved", object_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
auto& obj = port.objects[object_id];
|
||||
|
||||
for (u64 i = 0; i < num_attributes; i++) {
|
||||
if (attribute_array[i].attribute_id ==
|
||||
OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_RESET_STATE) {
|
||||
for (auto& data : obj.pcm_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
obj.pcm_queue.clear();
|
||||
obj.persistent_attributes.clear();
|
||||
LOG_DEBUG(Lib_Audio3d, "RESET_STATE for object {}", object_id);
|
||||
break; // Only one reset is needed even if listed multiple times.
|
||||
}
|
||||
}
|
||||
|
||||
// apply all other attributes.
|
||||
for (u64 i = 0; i < num_attributes; i++) {
|
||||
const auto& attribute = attribute_array[i];
|
||||
|
||||
switch (attribute.attribute_id) {
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_RESET_STATE:
|
||||
break; // Already applied in first pass above.
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_PCM: {
|
||||
if (attribute.value_size < sizeof(OrbisAudio3dPcm)) {
|
||||
LOG_ERROR(Lib_Audio3d, "PCM attribute value_size too small");
|
||||
continue;
|
||||
}
|
||||
const auto pcm = static_cast<OrbisAudio3dPcm*>(attribute.value);
|
||||
// Object audio has 1 channel.
|
||||
if (const auto ret = PortQueueAudio(port, *pcm, 1); ret != ORBIS_OK) {
|
||||
// Object audio is always mono (1 channel).
|
||||
if (const auto ret =
|
||||
ConvertAndEnqueue(obj.pcm_queue, *pcm, 1, port.parameters.granularity);
|
||||
ret != ORBIS_OK) {
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Lib_Audio3d, "Unsupported attribute ID: {:#x}",
|
||||
static_cast<u32>(attribute.attribute_id));
|
||||
default: {
|
||||
// store the other attributes in the ObjectState so they're available when we implement
|
||||
// them
|
||||
if (attribute.value && attribute.value_size > 0) {
|
||||
const auto* src = static_cast<const u8*>(attribute.value);
|
||||
obj.persistent_attributes[static_cast<u32>(attribute.attribute_id)].assign(
|
||||
src, src + attribute.value_size);
|
||||
}
|
||||
LOG_DEBUG(Lib_Audio3d, "Stored attribute {:#x} for object {}",
|
||||
static_cast<u32>(attribute.attribute_id), object_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(const OrbisAudio3dPortId port_id,
|
||||
const OrbisAudio3dObjectId object_id) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, object_id = {}", port_id, object_id);
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
|
||||
if (!port.objects.contains(object_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "object_id not reserved");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_OBJECT;
|
||||
}
|
||||
|
||||
// Free any queued PCM audio for this object.
|
||||
for (auto& data : port.objects[object_id].pcm_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
|
||||
port.objects.erase(object_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -320,32 +456,164 @@ s32 PS4_SYSV_ABI sceAudio3dPortAdvance(const OrbisAudio3dPortId port_id) {
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
if (state->ports[port_id].parameters.buffer_mode ==
|
||||
OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE) {
|
||||
auto& port = state->ports[port_id];
|
||||
|
||||
if (port.parameters.buffer_mode == OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE) {
|
||||
LOG_ERROR(Lib_Audio3d, "port doesn't have advance capability");
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
if (port.current_buffer.has_value()) {
|
||||
// Free existing buffer before replacing.
|
||||
SDL_free(port.current_buffer->sample_buffer);
|
||||
if (port.mixed_queue.size() >= port.parameters.queue_depth) {
|
||||
LOG_WARNING(Lib_Audio3d, "mixed queue full (depth={}), dropping advance",
|
||||
port.parameters.queue_depth);
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
if (!port.queue.empty()) {
|
||||
port.current_buffer = port.queue.front();
|
||||
port.queue.pop_front();
|
||||
} else {
|
||||
// Nothing to advance to.
|
||||
LOG_DEBUG(Lib_Audio3d, "Port advance with no buffer queued");
|
||||
port.current_buffer = std::nullopt;
|
||||
const u32 granularity = port.parameters.granularity;
|
||||
const u32 out_samples = granularity * AUDIO3D_OUTPUT_NUM_CHANNELS;
|
||||
|
||||
// ---- FLOAT MIX BUFFER ----
|
||||
float* mix_float = static_cast<float*>(std::calloc(out_samples, sizeof(float)));
|
||||
|
||||
if (!mix_float)
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
auto mix_in = [&](std::deque<AudioData>& queue, const float gain) {
|
||||
if (queue.empty())
|
||||
return;
|
||||
|
||||
// default gain is 0.0 — objects with no GAIN set are silent.
|
||||
if (gain == 0.0f) {
|
||||
AudioData data = queue.front();
|
||||
queue.pop_front();
|
||||
std::free(data.sample_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
AudioData data = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
const u32 frames = std::min(granularity, data.num_samples);
|
||||
const u32 channels = data.num_channels;
|
||||
|
||||
if (data.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
|
||||
const s16* src = reinterpret_cast<const s16*>(data.sample_buffer);
|
||||
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
float left = 0.0f;
|
||||
float right = 0.0f;
|
||||
|
||||
if (channels == 1) {
|
||||
float v = src[i] / 32768.0f;
|
||||
left = v;
|
||||
right = v;
|
||||
} else {
|
||||
left = src[i * channels + 0] / 32768.0f;
|
||||
right = src[i * channels + 1] / 32768.0f;
|
||||
}
|
||||
|
||||
mix_float[i * 2 + 0] += left * gain;
|
||||
mix_float[i * 2 + 1] += right * gain;
|
||||
}
|
||||
} else { // FLOAT input
|
||||
const float* src = reinterpret_cast<const float*>(data.sample_buffer);
|
||||
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
float left = 0.0f;
|
||||
float right = 0.0f;
|
||||
|
||||
if (channels == 1) {
|
||||
left = src[i];
|
||||
right = src[i];
|
||||
} else {
|
||||
left = src[i * channels + 0];
|
||||
right = src[i * channels + 1];
|
||||
}
|
||||
|
||||
mix_float[i * 2 + 0] += left * gain;
|
||||
mix_float[i * 2 + 1] += right * gain;
|
||||
}
|
||||
}
|
||||
|
||||
std::free(data.sample_buffer);
|
||||
};
|
||||
|
||||
// Bed is mixed at full gain (1.0)
|
||||
mix_in(port.bed_queue, 1.0f);
|
||||
|
||||
// Mix all object PCM queues, applying each object's GAIN persistent attribute.
|
||||
for (auto& [obj_id, obj] : port.objects) {
|
||||
float gain = 0.0f;
|
||||
const auto gain_key =
|
||||
static_cast<u32>(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_GAIN);
|
||||
if (obj.persistent_attributes.contains(gain_key)) {
|
||||
const auto& blob = obj.persistent_attributes.at(gain_key);
|
||||
if (blob.size() >= sizeof(float)) {
|
||||
std::memcpy(&gain, blob.data(), sizeof(float));
|
||||
}
|
||||
}
|
||||
mix_in(obj.pcm_queue, gain);
|
||||
}
|
||||
|
||||
s16* mix_s16 = static_cast<s16*>(std::malloc(out_samples * sizeof(s16)));
|
||||
|
||||
if (!mix_s16) {
|
||||
std::free(mix_float);
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < out_samples; i++) {
|
||||
float v = std::clamp(mix_float[i], -1.0f, 1.0f);
|
||||
mix_s16[i] = static_cast<s16>(v * 32767.0f);
|
||||
}
|
||||
|
||||
std::free(mix_float);
|
||||
|
||||
port.mixed_queue.push_back(AudioData{.sample_buffer = reinterpret_cast<u8*>(mix_s16),
|
||||
.num_samples = granularity,
|
||||
.num_channels = AUDIO3D_OUTPUT_NUM_CHANNELS,
|
||||
.format = OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortClose(const OrbisAudio3dPortId port_id) {
|
||||
LOG_INFO(Lib_Audio3d, "called, port_id = {}", port_id);
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortClose() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
{
|
||||
std::scoped_lock lock{port.mutex};
|
||||
|
||||
if (port.audio_out_handle >= 0) {
|
||||
AudioOut::sceAudioOutClose(port.audio_out_handle);
|
||||
port.audio_out_handle = -1;
|
||||
}
|
||||
|
||||
for (const s32 handle : port.audioout_handles) {
|
||||
AudioOut::sceAudioOutClose(handle);
|
||||
}
|
||||
port.audioout_handles.clear();
|
||||
|
||||
for (auto& data : port.mixed_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
|
||||
for (auto& data : port.bed_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
|
||||
for (auto& [obj_id, obj] : port.objects) {
|
||||
for (auto& data : obj.pcm_queue) {
|
||||
std::free(data.sample_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->ports.erase(port_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -359,8 +627,65 @@ s32 PS4_SYSV_ABI sceAudio3dPortDestroy() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFlush() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFlush(const OrbisAudio3dPortId port_id) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}", port_id);
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
|
||||
if (!port.audioout_handles.empty()) {
|
||||
for (const s32 handle : port.audioout_handles) {
|
||||
const s32 ret = AudioOut::sceAudioOutOutput(handle, nullptr);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
if (port.mixed_queue.empty()) {
|
||||
// Only mix if there's actually something to mix.
|
||||
if (!port.bed_queue.empty() ||
|
||||
std::any_of(port.objects.begin(), port.objects.end(),
|
||||
[](const auto& kv) { return !kv.second.pcm_queue.empty(); })) {
|
||||
const s32 ret = sceAudio3dPortAdvance(port_id);
|
||||
if (ret != ORBIS_OK && ret != ORBIS_AUDIO3D_ERROR_NOT_READY) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (port.mixed_queue.empty()) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
if (port.audio_out_handle < 0) {
|
||||
AudioOut::OrbisAudioOutParamExtendedInformation ext_info{};
|
||||
ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT);
|
||||
port.audio_out_handle =
|
||||
AudioOut::sceAudioOutOpen(0xFF, AudioOut::OrbisAudioOutPort::Audio3d, 0,
|
||||
port.parameters.granularity, AUDIO3D_SAMPLE_RATE, ext_info);
|
||||
if (port.audio_out_handle < 0) {
|
||||
return port.audio_out_handle;
|
||||
}
|
||||
}
|
||||
|
||||
// Drain all queued mixed frames, blocking on each until consumed.
|
||||
while (!port.mixed_queue.empty()) {
|
||||
AudioData frame = port.mixed_queue.front();
|
||||
port.mixed_queue.pop_front();
|
||||
const s32 ret = AudioOut::sceAudioOutOutput(port.audio_out_handle, frame.sample_buffer);
|
||||
std::free(frame.sample_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -398,15 +723,17 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(const OrbisAudio3dPortId port_id, u
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const auto port = state->ports[port_id];
|
||||
const size_t size = port.queue.size();
|
||||
const auto& port = state->ports[port_id];
|
||||
std::scoped_lock lock{port.mutex};
|
||||
const size_t size = port.mixed_queue.size();
|
||||
|
||||
if (queue_level) {
|
||||
*queue_level = size;
|
||||
*queue_level = static_cast<u32>(size);
|
||||
}
|
||||
|
||||
if (queue_available) {
|
||||
*queue_available = port.parameters.queue_depth - size;
|
||||
const u32 depth = port.parameters.queue_depth;
|
||||
*queue_available = (size < depth) ? static_cast<u32>(depth - size) : 0u;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
@ -446,7 +773,10 @@ s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServi
|
||||
}
|
||||
|
||||
*port_id = id;
|
||||
std::memcpy(&state->ports[id].parameters, parameters, parameters->size_this);
|
||||
auto& port = state->ports[id];
|
||||
std::memcpy(
|
||||
&port.parameters, parameters,
|
||||
std::min(parameters->size_this, static_cast<u64>(sizeof(OrbisAudio3dOpenParameters))));
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -461,24 +791,96 @@ s32 PS4_SYSV_ABI sceAudio3dPortPush(const OrbisAudio3dPortId port_id,
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
const auto& port = state->ports[port_id];
|
||||
auto& port = state->ports[port_id];
|
||||
|
||||
if (port.parameters.buffer_mode !=
|
||||
OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH) {
|
||||
LOG_ERROR(Lib_Audio3d, "port doesn't have push capability");
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!port.current_buffer.has_value()) {
|
||||
// Nothing to push.
|
||||
LOG_DEBUG(Lib_Audio3d, "Port push with no buffer ready");
|
||||
const u32 depth = port.parameters.queue_depth;
|
||||
|
||||
if (port.audio_out_handle < 0) {
|
||||
AudioOut::OrbisAudioOutParamExtendedInformation ext_info{};
|
||||
ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT);
|
||||
|
||||
port.audio_out_handle =
|
||||
AudioOut::sceAudioOutOpen(0xFF, AudioOut::OrbisAudioOutPort::Audio3d, 0,
|
||||
port.parameters.granularity, AUDIO3D_SAMPLE_RATE, ext_info);
|
||||
|
||||
if (port.audio_out_handle < 0)
|
||||
return port.audio_out_handle;
|
||||
}
|
||||
|
||||
// Function that submits exactly one frame (if available)
|
||||
auto submit_one_frame = [&](bool& submitted) -> s32 {
|
||||
AudioData frame;
|
||||
{
|
||||
std::scoped_lock lock{port.mutex};
|
||||
|
||||
if (port.mixed_queue.empty()) {
|
||||
submitted = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
frame = port.mixed_queue.front();
|
||||
port.mixed_queue.pop_front();
|
||||
}
|
||||
|
||||
const s32 ret = AudioOut::sceAudioOutOutput(port.audio_out_handle, frame.sample_buffer);
|
||||
|
||||
std::free(frame.sample_buffer);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
submitted = true;
|
||||
return ORBIS_OK;
|
||||
};
|
||||
|
||||
// if not full, return immediately
|
||||
{
|
||||
std::scoped_lock lock{port.mutex};
|
||||
if (port.mixed_queue.size() < depth) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Submit one frame to free space
|
||||
bool submitted = false;
|
||||
s32 ret = submit_one_frame(submitted);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!submitted)
|
||||
return ORBIS_OK;
|
||||
|
||||
// ASYNC: free exactly one slot and return
|
||||
if (blocking == OrbisAudio3dBlocking::ORBIS_AUDIO3D_BLOCKING_ASYNC) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
// TODO: Implement asynchronous blocking mode.
|
||||
const auto& [sample_buffer, num_samples] = port.current_buffer.value();
|
||||
return AudioOut::sceAudioOutOutput(state->audio_out_handle, sample_buffer);
|
||||
}
|
||||
// SYNC: ensure at least one slot is free
|
||||
// (drain until size < depth)
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lock{port.mutex};
|
||||
if (port.mixed_queue.size() < depth)
|
||||
break;
|
||||
}
|
||||
|
||||
bool drained = false;
|
||||
ret = submit_one_frame(drained);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!drained)
|
||||
break;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
@ -532,9 +934,15 @@ s32 PS4_SYSV_ABI sceAudio3dTerminate() {
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
AudioOut::sceAudioOutOutput(state->audio_out_handle, nullptr);
|
||||
AudioOut::sceAudioOutClose(state->audio_out_handle);
|
||||
state.release();
|
||||
std::vector<OrbisAudio3dPortId> port_ids;
|
||||
for (const auto& [id, _] : state->ports) {
|
||||
port_ids.push_back(id);
|
||||
}
|
||||
for (const auto id : port_ids) {
|
||||
sceAudio3dPortClose(id);
|
||||
}
|
||||
|
||||
state.reset();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -557,6 +965,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
sceAudio3dGetSpeakerArrayMixCoefficients2);
|
||||
LIB_FUNCTION("UmCvjSmuZIw", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dInitialize);
|
||||
LIB_FUNCTION("jO2tec4dJ2M", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectReserve);
|
||||
LIB_FUNCTION("V1FBFpNIAzk", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectSetAttribute);
|
||||
LIB_FUNCTION("4uyHN9q4ZeU", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectSetAttributes);
|
||||
LIB_FUNCTION("1HXxo-+1qCw", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectUnreserve);
|
||||
LIB_FUNCTION("lw0qrdSjZt8", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortAdvance);
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/types.h"
|
||||
@ -15,6 +18,8 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::Audio3d {
|
||||
|
||||
constexpr int ORBIS_AUDIO3D_OBJECT_INVALID = 0xFFFFFFFF;
|
||||
|
||||
enum class OrbisAudio3dRate : u32 {
|
||||
ORBIS_AUDIO3D_RATE_48000 = 0,
|
||||
};
|
||||
@ -60,10 +65,21 @@ struct OrbisAudio3dPcm {
|
||||
|
||||
enum class OrbisAudio3dAttributeId : u32 {
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_PCM = 1,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_POSITION = 2,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_GAIN = 3,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_SPREAD = 4,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_PRIORITY = 5,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_PASSTHROUGH = 6,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_AMBISONICS = 7,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_APPLICATION_SPECIFIC = 8,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_RESET_STATE = 9,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_RESTRICTED = 10,
|
||||
ORBIS_AUDIO3D_ATTRIBUTE_OUTPUT_ROUTE = 11,
|
||||
};
|
||||
|
||||
using OrbisAudio3dPortId = u32;
|
||||
using OrbisAudio3dObjectId = u32;
|
||||
using OrbisAudio3dAmbisonics = u32;
|
||||
|
||||
struct OrbisAudio3dAttribute {
|
||||
OrbisAudio3dAttributeId attribute_id;
|
||||
@ -75,17 +91,35 @@ struct OrbisAudio3dAttribute {
|
||||
struct AudioData {
|
||||
u8* sample_buffer;
|
||||
u32 num_samples;
|
||||
u32 num_channels{1}; // channels in sample_buffer
|
||||
OrbisAudio3dFormat format{
|
||||
OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16}; // format of sample_buffer
|
||||
};
|
||||
|
||||
struct ObjectState {
|
||||
std::deque<AudioData> pcm_queue;
|
||||
std::unordered_map<u32, std::vector<u8>> persistent_attributes;
|
||||
};
|
||||
|
||||
struct Port {
|
||||
mutable std::recursive_mutex mutex;
|
||||
OrbisAudio3dOpenParameters parameters{};
|
||||
std::deque<AudioData> queue; // Only stores PCM buffers for now
|
||||
std::optional<AudioData> current_buffer{};
|
||||
// Opened lazily on the first sceAudio3dPortPush call.
|
||||
s32 audio_out_handle{-1};
|
||||
// Handles explicitly opened by the game via sceAudio3dAudioOutOpen.
|
||||
std::vector<s32> audioout_handles;
|
||||
// Reserved objects and their state.
|
||||
std::unordered_map<OrbisAudio3dObjectId, ObjectState> objects;
|
||||
// increasing counter for generating unique object IDs within this port.
|
||||
OrbisAudio3dObjectId next_object_id{0};
|
||||
// Bed audio queue
|
||||
std::deque<AudioData> bed_queue;
|
||||
// Mixed stereo frames ready to be consumed by sceAudio3dPortPush.
|
||||
std::deque<AudioData> mixed_queue;
|
||||
};
|
||||
|
||||
struct Audio3dState {
|
||||
std::unordered_map<OrbisAudio3dPortId, Port> ports;
|
||||
s32 audio_out_handle;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle);
|
||||
@ -109,15 +143,20 @@ s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2();
|
||||
s32 PS4_SYSV_ABI sceAudio3dInitialize(s64 reserved);
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectReserve(OrbisAudio3dPortId port_id,
|
||||
OrbisAudio3dObjectId* object_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectSetAttribute(OrbisAudio3dPortId port_id,
|
||||
OrbisAudio3dObjectId object_id,
|
||||
OrbisAudio3dAttributeId attribute_id,
|
||||
const void* attribute, u64 attribute_size);
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId port_id,
|
||||
OrbisAudio3dObjectId object_id, u64 num_attributes,
|
||||
const OrbisAudio3dAttribute* attribute_array);
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve();
|
||||
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(OrbisAudio3dPortId port_id,
|
||||
OrbisAudio3dObjectId object_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId port_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortClose();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortClose(OrbisAudio3dPortId port_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortCreate();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortDestroy();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFlush();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId port_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFreeState();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetList();
|
||||
|
||||
@ -12,28 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto allocate = self->m_init_data_original.memory_replacement.allocate;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(allocate, ptr, alignment, size);
|
||||
return allocate(ptr, alignment, size);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(deallocate, ptr, memory);
|
||||
return deallocate(ptr, memory);
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(allocate, ptr, alignment, size);
|
||||
return allocate(ptr, alignment, size);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(deallocate, ptr, memory);
|
||||
return deallocate(ptr, memory);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
|
||||
@ -42,7 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
|
||||
|
||||
const auto open = self->m_init_data_original.file_replacement.open;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(open, ptr, filename);
|
||||
return open(ptr, filename);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
|
||||
@ -51,7 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
|
||||
|
||||
const auto close = self->m_init_data_original.file_replacement.close;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(close, ptr);
|
||||
return close(ptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) {
|
||||
@ -60,7 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
|
||||
|
||||
const auto read_offset = self->m_init_data_original.file_replacement.read_offset;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(read_offset, ptr, buffer, position, length);
|
||||
return read_offset(ptr, buffer, position, length);
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||
@ -69,7 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||
|
||||
const auto size = self->m_init_data_original.file_replacement.size;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return Core::ExecuteGuest(size, ptr);
|
||||
return size(ptr);
|
||||
}
|
||||
|
||||
AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) {
|
||||
|
||||
@ -92,7 +92,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, AvPlayerEvents event_id,
|
||||
const auto callback = self->m_event_replacement.event_callback;
|
||||
const auto ptr = self->m_event_replacement.object_ptr;
|
||||
if (callback != nullptr) {
|
||||
Core::ExecuteGuest(callback, ptr, event_id, 0, event_data);
|
||||
callback(ptr, event_id, 0, event_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -627,10 +627,30 @@ int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmDrawIndirectMulti() {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
UNREACHABLE();
|
||||
return ORBIS_OK;
|
||||
s32 PS4_SYSV_ABI sceGnmDrawIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count,
|
||||
u32 shader_stage, u32 vertex_sgpr_offset,
|
||||
u32 instance_sgpr_offset, u32 flags) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (cmdbuf && size == 11 && shader_stage < ShaderStages::Max && vertex_sgpr_offset < 0x10 &&
|
||||
instance_sgpr_offset < 0x10) {
|
||||
const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable;
|
||||
cmdbuf = WriteHeader<PM4ItOpcode::DrawIndirectMulti>(
|
||||
cmdbuf, 4, PM4ShaderType::ShaderGraphics, predicate);
|
||||
|
||||
const auto sgpr_offset = indirect_sgpr_offsets[shader_stage];
|
||||
cmdbuf[0] = data_offset;
|
||||
cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset;
|
||||
cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset;
|
||||
cmdbuf[3] = max_count;
|
||||
cmdbuf[4] = sizeof(DrawIndirectArgs);
|
||||
cmdbuf[5] = sceKernelIsNeoMode() ? flags & 0xe0000000u | 2u : 2u; // auto index
|
||||
|
||||
cmdbuf += 6;
|
||||
WriteTrailingNop<3>(cmdbuf);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) {
|
||||
|
||||
@ -60,7 +60,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset,
|
||||
s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage,
|
||||
u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags);
|
||||
int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti();
|
||||
int PS4_SYSV_ABI sceGnmDrawIndirectMulti();
|
||||
s32 PS4_SYSV_ABI sceGnmDrawIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count,
|
||||
u32 shader_stage, u32 vertex_sgpr_offset,
|
||||
u32 instance_sgpr_offset, u32 flags);
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size);
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size);
|
||||
u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size);
|
||||
|
||||
@ -100,16 +100,16 @@ public:
|
||||
if (m_ime_mode) {
|
||||
OrbisImeParam param = m_param.ime;
|
||||
if (use_param_handler) {
|
||||
Core::ExecuteGuest(param.handler, param.arg, event);
|
||||
param.handler(param.arg, event);
|
||||
} else {
|
||||
Core::ExecuteGuest(handler, param.arg, event);
|
||||
handler(param.arg, event);
|
||||
}
|
||||
} else {
|
||||
OrbisImeKeyboardParam param = m_param.key;
|
||||
if (use_param_handler) {
|
||||
Core::ExecuteGuest(param.handler, param.arg, event);
|
||||
param.handler(param.arg, event);
|
||||
} else {
|
||||
Core::ExecuteGuest(handler, param.arg, event);
|
||||
handler(param.arg, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,8 +348,7 @@ bool ImeDialogState::CallTextFilter() {
|
||||
src_text_length = static_cast<u32>(
|
||||
BoundedUtf16Length(src_text, static_cast<size_t>(ORBIS_IME_DIALOG_MAX_TEXT_LENGTH)));
|
||||
|
||||
int ret =
|
||||
Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length);
|
||||
int ret = text_filter(out_text, &out_text_length, src_text, src_text_length);
|
||||
|
||||
if (ret != 0) {
|
||||
return false;
|
||||
@ -381,7 +380,7 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16*
|
||||
return true;
|
||||
}
|
||||
|
||||
int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr);
|
||||
int ret = keyboard_filter(src_keycode, out_keycode, out_status, nullptr);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,17 @@
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
void PS4_SYSV_ABI ClearStack() {
|
||||
void* const stackaddr_attr = Libraries::Kernel::g_curthread->attr.stackaddr_attr;
|
||||
void* volatile sp;
|
||||
asm("mov %%rsp, %0" : "=rm"(sp));
|
||||
// leave a safety net of 64 bytes for memset
|
||||
const size_t size = ((uintptr_t)sp - (uintptr_t)stackaddr_attr) - 64;
|
||||
void* volatile buf = alloca(size);
|
||||
memset(buf, 0, size);
|
||||
sp = nullptr;
|
||||
}
|
||||
|
||||
void RegisterThreads(Core::Loader::SymbolsResolver* sym) {
|
||||
RegisterMutex(sym);
|
||||
RegisterCond(sym);
|
||||
|
||||
@ -27,6 +27,7 @@ int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr
|
||||
PthreadEntryFunc start_routine, void* arg);
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return);
|
||||
int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread);
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr);
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type);
|
||||
@ -40,6 +41,8 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex);
|
||||
|
||||
void RegisterThreads(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
void PS4_SYSV_ABI ClearStack();
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
explicit Thread() = default;
|
||||
|
||||
@ -317,7 +317,7 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||
|
||||
u64 res = NtQueueApcThreadEx(reinterpret_cast<HANDLE>(thread->native_thr.GetHandle()), option,
|
||||
ExceptionHandler, (void*)thread->name.c_str(),
|
||||
(void*)native_signum, nullptr);
|
||||
(void*)(s64)native_signum, nullptr);
|
||||
ASSERT(res == 0);
|
||||
#endif
|
||||
return ORBIS_OK;
|
||||
|
||||
@ -199,10 +199,17 @@ static void RunThread(void* arg) {
|
||||
g_curthread = curthread;
|
||||
Common::SetCurrentThreadName(curthread->name.c_str());
|
||||
DebugState.AddCurrentThreadToGuestList();
|
||||
Core::InitializeTLS();
|
||||
|
||||
curthread->native_thr.Initialize();
|
||||
|
||||
// Clear the stack before running the guest thread
|
||||
if (False(g_curthread->attr.flags & PthreadAttrFlags::StackUser)) {
|
||||
ClearStack();
|
||||
}
|
||||
|
||||
/* Run the current thread's start routine with argument: */
|
||||
curthread->native_thr.Initialize();
|
||||
void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg);
|
||||
void* ret = curthread->start_routine(curthread->arg);
|
||||
|
||||
/* Remove thread from tracking */
|
||||
DebugState.RemoveCurrentThreadFromGuestList();
|
||||
|
||||
@ -84,7 +84,7 @@ void _thread_cleanupspecific() {
|
||||
* destructor:
|
||||
*/
|
||||
lk.unlock();
|
||||
Core::ExecuteGuest(destructor, data);
|
||||
destructor(data);
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -935,18 +935,24 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
pathLength++;
|
||||
}
|
||||
|
||||
// Ensure the path starts with '/'
|
||||
if (pathLength > 0 && pathStart[0] != '/') {
|
||||
if (pathLength > 0) {
|
||||
// Prepend '/' to the path
|
||||
requiredSize += pathLength + 2; // Include '/' and null terminator
|
||||
|
||||
if (pool && prepare < requiredSize) {
|
||||
LOG_ERROR(Lib_Http, "out of memory");
|
||||
LOG_ERROR(Lib_Http, "out of memory, provided size: {}, required size: {}",
|
||||
prepare, requiredSize);
|
||||
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (out && pool) {
|
||||
out->path = (char*)pool + (requiredSize - pathLength - 2);
|
||||
out->username = (char*)pool + (requiredSize - pathLength - 3);
|
||||
out->password = (char*)pool + (requiredSize - pathLength - 3);
|
||||
out->hostname = (char*)pool + (requiredSize - pathLength - 3);
|
||||
out->query = (char*)pool + (requiredSize - pathLength - 3);
|
||||
out->fragment = (char*)pool + (requiredSize - pathLength - 3);
|
||||
out->username[0] = '\0';
|
||||
out->path[0] = '/'; // Add leading '/'
|
||||
memcpy(out->path + 1, pathStart, pathLength);
|
||||
out->path[pathLength + 1] = '\0';
|
||||
@ -969,6 +975,19 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
|
||||
// Move past the path
|
||||
offset += pathLength;
|
||||
} else {
|
||||
// Parse the path (everything after the slashes)
|
||||
char* pathStart = (char*)srcUri + offset;
|
||||
u64 pathLength = 0;
|
||||
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
||||
pathStart[pathLength] != '#') {
|
||||
pathLength++;
|
||||
}
|
||||
|
||||
if (pathLength > 0) {
|
||||
requiredSize += pathLength + 3; // Add '/' and null terminator, and the dummy
|
||||
// null character for the other fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -887,6 +887,10 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events,
|
||||
}
|
||||
|
||||
file->resolver->Resolve();
|
||||
if (file->resolver->resolution_error != ORBIS_OK) {
|
||||
// Resolution failed, shouldn't appear.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto it =
|
||||
std::ranges::find_if(epoll->events, [&](auto& el) { return el.first == rid; });
|
||||
@ -1402,8 +1406,21 @@ int PS4_SYSV_ABI sceNetResolverDestroy(OrbisNetId resolverid) {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetResolverGetError(OrbisNetId resolverid, s32* status) {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called rid = {}", resolverid);
|
||||
*status = 0;
|
||||
if (!status) {
|
||||
LOG_ERROR(Lib_Net, "status == nullptr");
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EINVAL;
|
||||
return ORBIS_NET_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto file = FDTable::Instance()->GetResolver(resolverid);
|
||||
if (!file) {
|
||||
LOG_ERROR(Lib_Net, "invalid resolverid {}", resolverid);
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
*status = file->resolver->resolution_error;
|
||||
LOG_INFO(Lib_Net, "called rid = {}, error = {:#x}", resolverid, static_cast<u32>(*status));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -1425,10 +1442,17 @@ int PS4_SYSV_ABI sceNetResolverStartNtoa(OrbisNetId resolverid, const char* host
|
||||
|
||||
auto file = FDTable::Instance()->GetResolver(resolverid);
|
||||
if (!file) {
|
||||
LOG_ERROR(Lib_Net, "invalid resolverid {}", resolverid);
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!Config::getIsConnectedToNetwork()) {
|
||||
*sceNetErrnoLoc() = ORBIS_NET_RESOLVER_ENODNS;
|
||||
file->resolver->resolution_error = ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
return ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
}
|
||||
|
||||
if ((flags & ORBIS_NET_RESOLVER_ASYNC) != 0) {
|
||||
return file->resolver->ResolveAsync(hostname, addr, timeout, retry, flags);
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ void NetCtlInternal::CheckCallback() {
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : callbacks) {
|
||||
if (func != nullptr) {
|
||||
Core::ExecuteGuest(func, event, arg);
|
||||
func(event, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,7 +61,7 @@ void NetCtlInternal::CheckNpToolkitCallback() {
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : nptool_callbacks) {
|
||||
if (func != nullptr) {
|
||||
Core::ExecuteGuest(func, event, arg);
|
||||
func(event, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
@ -26,11 +27,19 @@ int Resolver::ResolveAsync(const char* hostname, OrbisNetInAddr* addr, int timeo
|
||||
}
|
||||
|
||||
void Resolver::Resolve() {
|
||||
if (!Config::getIsConnectedToNetwork()) {
|
||||
resolution_error = ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (async_resolution) {
|
||||
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
|
||||
auto ret = netinfo->ResolveHostname(async_resolution->hostname, async_resolution->addr);
|
||||
|
||||
resolution_error = ret;
|
||||
if (ret != ORBIS_OK) {
|
||||
// Resolver errors are stored as ORBIS_NET_ERROR values.
|
||||
resolution_error = -ret | ORBIS_NET_ERROR_BASE;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Lib_Net, "async resolution has not been set-up");
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ public:
|
||||
int ResolveAsync(const char* hostname, OrbisNetInAddr* addr, int timeout, int retry, int flags);
|
||||
void Resolve();
|
||||
|
||||
int resolution_error = ORBIS_OK;
|
||||
|
||||
private:
|
||||
struct AsyncResolution {
|
||||
const char* hostname;
|
||||
@ -31,7 +33,6 @@ private:
|
||||
int poolid;
|
||||
int flags;
|
||||
std::optional<AsyncResolution> async_resolution{};
|
||||
int resolution_error = ORBIS_OK;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
|
||||
@ -430,6 +430,15 @@ int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
|
||||
sockaddr addr2;
|
||||
convertOrbisNetSockaddrToPosix(addr, &addr2);
|
||||
int result = ::connect(sock, &addr2, sizeof(sockaddr_in));
|
||||
#ifdef _WIN32
|
||||
// Winsock returns EWOULDBLOCK where real hardware returns EINPROGRESS
|
||||
// Step in here on errors to address this.
|
||||
if (result == -1) {
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK) {
|
||||
WSASetLastError(WSAEINPROGRESS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
LOG_DEBUG(Lib_Net, "raw connect result = {}, errno = {}", result,
|
||||
result == -1 ? Common::GetLastErrorMsg() : "none");
|
||||
return ConvertReturnErrorCode(result);
|
||||
|
||||
@ -28,8 +28,11 @@ int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 add
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name,
|
||||
(u32)*Libraries::Kernel::__Error());
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
// Don't log EINPROGRESS or EISCONN, these are normal to see from non-blocking communication.
|
||||
if (error != ORBIS_NET_EINPROGRESS && error != ORBIS_NET_EISCONN) {
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -59,8 +62,13 @@ int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen)
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
auto new_sock = file->socket->Accept(addr, paddrlen);
|
||||
if (!new_sock) {
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code creating new socket for accepting: {}",
|
||||
s, file->m_guest_name, (u32)*Libraries::Kernel::__Error());
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
// Don't log EWOULDBLOCK, this is normal to see from non-blocking communication.
|
||||
if (error != ORBIS_NET_EWOULDBLOCK) {
|
||||
LOG_ERROR(Lib_Net,
|
||||
"s = {} ({}) returned error code creating new socket for accepting: {}", s,
|
||||
file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
auto fd = FDTable::Instance()->CreateHandle();
|
||||
@ -396,8 +404,11 @@ s64 PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, Orbis
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name,
|
||||
(u32)*Libraries::Kernel::__Error());
|
||||
// Don't log EWOULDBLOCK, this is normal to see from non-blocking communication.
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
if (error != ORBIS_NET_EWOULDBLOCK) {
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@ -160,13 +160,13 @@ s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* o
|
||||
result = SystemSetup(option, &bufferInfo, 0, 0);
|
||||
if (result >= 0) {
|
||||
uintptr_t sysUserData = allocator->userData;
|
||||
result = Core::ExecuteGuest(hostAlloc, &bufferInfo);
|
||||
result = hostAlloc(&bufferInfo);
|
||||
if (result >= 0) {
|
||||
OrbisNgs2Handle* handleCopy = outHandle;
|
||||
result = SystemSetup(option, &bufferInfo, hostFree, handleCopy);
|
||||
if (result < 0) {
|
||||
if (hostFree) {
|
||||
Core::ExecuteGuest(hostFree, &bufferInfo);
|
||||
hostFree(&bufferInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +333,9 @@ static bool match(std::string_view str, std::string_view pattern) {
|
||||
for (auto str_wild_it = str_it; str_wild_it <= str.end(); ++str_wild_it) {
|
||||
if (match({str_wild_it, str.end()}, {pat_it + 1, pattern.end()})) {
|
||||
return true;
|
||||
} else if (str_wild_it == str.end()) {
|
||||
// Avoid incrementing str_wild_it past str.end().
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
|
||||
#include "dimensions.h"
|
||||
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
@ -619,22 +622,46 @@ libusb_transfer_status DimensionsBackend::HandleAsyncTransfer(libusb_transfer* t
|
||||
return LIBUSB_TRANSFER_COMPLETED;
|
||||
}
|
||||
|
||||
struct WriteThreadArgs {
|
||||
DimensionsBackend* self;
|
||||
libusb_transfer* transfer;
|
||||
};
|
||||
|
||||
void* PS4_SYSV_ABI DimensionsBackend::WriteThread(void* arg) {
|
||||
auto* args = reinterpret_cast<WriteThreadArgs*>(arg);
|
||||
|
||||
auto* self = args->self;
|
||||
auto* transfer = args->transfer;
|
||||
|
||||
self->HandleAsyncTransfer(transfer);
|
||||
|
||||
const u8 flags = transfer->flags;
|
||||
transfer->status = LIBUSB_TRANSFER_COMPLETED;
|
||||
transfer->actual_length = transfer->length;
|
||||
if (transfer->callback) {
|
||||
transfer->callback(transfer);
|
||||
}
|
||||
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) {
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
delete args;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s32 DimensionsBackend::SubmitTransfer(libusb_transfer* transfer) {
|
||||
if (transfer->endpoint == 0x01) {
|
||||
std::thread write_thread([this, transfer] {
|
||||
HandleAsyncTransfer(transfer);
|
||||
using namespace Libraries::Kernel;
|
||||
|
||||
PthreadAttrT attr{};
|
||||
posix_pthread_attr_init(&attr);
|
||||
PthreadT thread{};
|
||||
auto* args = new WriteThreadArgs();
|
||||
args->self = this;
|
||||
args->transfer = transfer;
|
||||
posix_pthread_create(&thread, &attr, HOST_CALL(DimensionsBackend::WriteThread), args);
|
||||
posix_pthread_attr_destroy(&attr);
|
||||
posix_pthread_detach(thread);
|
||||
|
||||
const u8 flags = transfer->flags;
|
||||
transfer->status = LIBUSB_TRANSFER_COMPLETED;
|
||||
transfer->actual_length = transfer->length;
|
||||
if (transfer->callback) {
|
||||
transfer->callback(transfer);
|
||||
}
|
||||
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) {
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
});
|
||||
write_thread.detach();
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -103,6 +103,8 @@ protected:
|
||||
std::queue<std::array<u8, 32>> m_queries;
|
||||
|
||||
private:
|
||||
static void* PS4_SYSV_ABI WriteThread(void* arg);
|
||||
|
||||
std::shared_ptr<DimensionsToypad> m_dimensions_toypad = std::make_shared<DimensionsToypad>();
|
||||
|
||||
std::array<u8, 9> m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00};
|
||||
|
||||
@ -135,7 +135,8 @@ void Linker::Execute(const std::vector<std::string>& args) {
|
||||
}
|
||||
}
|
||||
params.entry_addr = module->GetEntryAddress();
|
||||
ExecuteGuest(RunMainEntry, ¶ms);
|
||||
Libraries::Kernel::ClearStack();
|
||||
RunMainEntry(¶ms);
|
||||
});
|
||||
}
|
||||
|
||||
@ -379,8 +380,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
|
||||
if (!addr) {
|
||||
// Module was just loaded by above code. Allocate TLS block for it.
|
||||
const u32 init_image_size = module->tls.init_image_size;
|
||||
u8* dest = reinterpret_cast<u8*>(
|
||||
Core::ExecuteGuest(heap_api->heap_malloc, module->tls.image_size));
|
||||
u8* dest = reinterpret_cast<u8*>(heap_api->heap_malloc(module->tls.image_size));
|
||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||
std::memcpy(dest, src, init_image_size);
|
||||
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
|
||||
@ -412,7 +412,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) {
|
||||
ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread");
|
||||
} else {
|
||||
if (heap_api) {
|
||||
addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size);
|
||||
addr_out = heap_api->heap_malloc(total_tls_size);
|
||||
} else {
|
||||
addr_out = std::malloc(total_tls_size);
|
||||
}
|
||||
@ -422,7 +422,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) {
|
||||
|
||||
void Linker::FreeTlsForNonPrimaryThread(void* pointer) {
|
||||
if (heap_api) {
|
||||
Core::ExecuteGuest(heap_api->heap_free, pointer);
|
||||
heap_api->heap_free(pointer);
|
||||
} else {
|
||||
std::free(pointer);
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ Module::~Module() = default;
|
||||
s32 Module::Start(u64 args, const void* argp, void* param) {
|
||||
LOG_INFO(Core_Linker, "Module started : {}", name);
|
||||
const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress();
|
||||
return ExecuteGuest(reinterpret_cast<EntryFunc>(addr), args, argp, param);
|
||||
return reinterpret_cast<EntryFunc>(addr)(args, argp, param);
|
||||
}
|
||||
|
||||
void Module::LoadModuleToMemory(u32& max_tls_index) {
|
||||
|
||||
@ -198,7 +198,7 @@ Tcb* GetTcbBase() {
|
||||
|
||||
thread_local std::once_flag init_tls_flag;
|
||||
|
||||
void EnsureThreadInitialized() {
|
||||
void InitializeTLS() {
|
||||
std::call_once(init_tls_flag, [] { SetTcbBase(Libraries::Kernel::g_curthread->tcb); });
|
||||
}
|
||||
|
||||
|
||||
@ -43,30 +43,7 @@ void SetTcbBase(void* image_address);
|
||||
Tcb* GetTcbBase();
|
||||
|
||||
/// Makes sure TLS is initialized for the thread before entering guest.
|
||||
void EnsureThreadInitialized();
|
||||
|
||||
template <size_t size>
|
||||
#ifdef __clang__
|
||||
__attribute__((optnone))
|
||||
#else
|
||||
__attribute__((optimize("O0")))
|
||||
#endif
|
||||
void ClearStack() {
|
||||
volatile void* buf = alloca(size);
|
||||
memset(const_cast<void*>(buf), 0, size);
|
||||
buf = nullptr;
|
||||
}
|
||||
|
||||
template <class ReturnType, class... FuncArgs, class... CallArgs>
|
||||
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
|
||||
EnsureThreadInitialized();
|
||||
// clear stack to avoid trash from EnsureThreadInitialized
|
||||
auto* tcb = GetTcbBase();
|
||||
if (tcb != nullptr && tcb->tcb_fiber == nullptr) {
|
||||
ClearStack<12_KB>();
|
||||
}
|
||||
return func(std::forward<CallArgs>(args)...);
|
||||
}
|
||||
void InitializeTLS();
|
||||
|
||||
template <class F, F f>
|
||||
struct HostCallWrapperImpl;
|
||||
|
||||
@ -245,7 +245,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
LOG_INFO(Config, "General isConnectedToNetwork: {}", Config::getIsConnectedToNetwork());
|
||||
LOG_INFO(Config, "General isPsnSignedIn: {}", Config::getPSNSignedIn());
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
|
||||
LOG_INFO(Config, "GPU readbacks: {}", Config::readbacks());
|
||||
LOG_INFO(Config, "GPU readbacksMode: {}", Config::getReadbacksMode());
|
||||
LOG_INFO(Config, "GPU readbackLinearImages: {}", Config::readbackLinearImages());
|
||||
LOG_INFO(Config, "GPU directMemoryAccess: {}", Config::directMemoryAccess());
|
||||
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
|
||||
|
||||
@ -229,6 +229,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
ce_task = ProcessCeUpdate(ccb);
|
||||
RESUME_GFX(ce_task);
|
||||
}
|
||||
const bool host_markers_enabled = rasterizer && Config::getVkHostMarkersEnabled();
|
||||
const bool guest_markers_enabled = rasterizer && Config::getVkGuestMarkersEnabled();
|
||||
|
||||
const auto base_addr = reinterpret_cast<uintptr_t>(dcb.data());
|
||||
while (!dcb.empty()) {
|
||||
@ -267,27 +269,27 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
break;
|
||||
}
|
||||
case PM4CmdNop::PayloadType::DebugMarkerPush: {
|
||||
const auto marker_sz = nop->header.count.Value() * 2;
|
||||
const std::string_view label{reinterpret_cast<const char*>(&nop->data_block[1]),
|
||||
marker_sz};
|
||||
if (rasterizer) {
|
||||
if (guest_markers_enabled) {
|
||||
const auto marker_sz = nop->header.count.Value() * 2;
|
||||
const std::string_view label{
|
||||
reinterpret_cast<const char*>(&nop->data_block[1]), marker_sz};
|
||||
rasterizer->ScopeMarkerBegin(label, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM4CmdNop::PayloadType::DebugColorMarkerPush: {
|
||||
const auto marker_sz = nop->header.count.Value() * 2;
|
||||
const std::string_view label{reinterpret_cast<const char*>(&nop->data_block[1]),
|
||||
marker_sz};
|
||||
const u32 color = *reinterpret_cast<const u32*>(
|
||||
reinterpret_cast<const u8*>(&nop->data_block[1]) + marker_sz);
|
||||
if (rasterizer) {
|
||||
if (guest_markers_enabled) {
|
||||
const auto marker_sz = nop->header.count.Value() * 2;
|
||||
const std::string_view label{
|
||||
reinterpret_cast<const char*>(&nop->data_block[1]), marker_sz};
|
||||
const u32 color = *reinterpret_cast<const u32*>(
|
||||
reinterpret_cast<const u8*>(&nop->data_block[1]) + marker_sz);
|
||||
rasterizer->ScopedMarkerInsertColor(label, color, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM4CmdNop::PayloadType::DebugMarkerPop: {
|
||||
if (rasterizer) {
|
||||
if (guest_markers_enabled) {
|
||||
rasterizer->ScopeMarkerEnd(true);
|
||||
}
|
||||
break;
|
||||
@ -427,9 +429,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndex2", cmd_address));
|
||||
rasterizer->Draw(true);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndex2", cmd_address));
|
||||
rasterizer->Draw(true);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->Draw(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -444,10 +450,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexOffset2", cmd_address));
|
||||
rasterizer->Draw(true, draw_index_off->index_offset);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexOffset2", cmd_address));
|
||||
rasterizer->Draw(true, draw_index_off->index_offset);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->Draw(true, draw_index_off->index_offset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -460,9 +470,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndexAuto", cmd_address));
|
||||
rasterizer->Draw(false);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexAuto", cmd_address));
|
||||
rasterizer->Draw(false);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->Draw(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -475,9 +490,36 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndirect", cmd_address));
|
||||
rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndirect", cmd_address));
|
||||
rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::DrawIndirectMulti: {
|
||||
const auto* draw_indirect =
|
||||
reinterpret_cast<const PM4CmdDrawIndirectMulti*>(header);
|
||||
const auto offset = draw_indirect->data_offset;
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndirectMulti", cmd_address));
|
||||
rasterizer->DrawIndirect(false, indirect_args_addr, offset,
|
||||
draw_indirect->stride, draw_indirect->count, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DrawIndirect(false, indirect_args_addr, offset,
|
||||
draw_indirect->stride, draw_indirect->count, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -491,10 +533,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirect", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirect", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -507,12 +553,18 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirectMulti", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirectMulti", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count, 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -525,15 +577,24 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count,
|
||||
draw_index_indirect->count_indirect_enable.Value()
|
||||
? draw_index_indirect->count_addr
|
||||
: 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address));
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count,
|
||||
draw_index_indirect->count_indirect_enable.Value()
|
||||
? draw_index_indirect->count_addr
|
||||
: 0);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DrawIndirect(true, indirect_args_addr, offset,
|
||||
draw_index_indirect->stride,
|
||||
draw_index_indirect->count,
|
||||
draw_index_indirect->count_indirect_enable.Value()
|
||||
? draw_index_indirect->count_addr
|
||||
: 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -550,9 +611,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DispatchDirect", cmd_address));
|
||||
rasterizer->DispatchDirect();
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DispatchDirect", cmd_address));
|
||||
rasterizer->DispatchDirect();
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DispatchDirect();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -568,10 +634,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DispatchIndirect", cmd_address));
|
||||
rasterizer->DispatchIndirect(indirect_args_addr, offset, size);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("gfx:{}:DispatchIndirect", cmd_address));
|
||||
rasterizer->DispatchIndirect(indirect_args_addr, offset, size);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DispatchIndirect(indirect_args_addr, offset, size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -829,6 +899,7 @@ template <bool is_indirect>
|
||||
Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
FIBER_ENTER(acb_task_name[vqid]);
|
||||
auto& queue = asc_queues[{vqid}];
|
||||
const bool host_markers_enabled = rasterizer && Config::getVkHostMarkersEnabled();
|
||||
|
||||
struct IndirectPatch {
|
||||
const PM4Header* header;
|
||||
@ -881,6 +952,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
}
|
||||
|
||||
const PM4ItOpcode opcode = header->type3.opcode;
|
||||
|
||||
const auto* it_body = reinterpret_cast<const u32*>(header) + 1;
|
||||
switch (opcode) {
|
||||
case PM4ItOpcode::Nop: {
|
||||
@ -998,10 +1070,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("asc[{}]:{}:DispatchDirect", vqid, cmd_address));
|
||||
rasterizer->DispatchDirect();
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("asc[{}]:{}:DispatchDirect", vqid, cmd_address));
|
||||
rasterizer->DispatchDirect();
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DispatchDirect();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1017,10 +1093,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("asc[{}]:{}:DispatchIndirect", vqid, cmd_address));
|
||||
rasterizer->DispatchIndirect(ib_address, 0, size);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (host_markers_enabled) {
|
||||
rasterizer->ScopeMarkerBegin(
|
||||
fmt::format("asc[{}]:{}:DispatchIndirect", vqid, cmd_address));
|
||||
rasterizer->DispatchIndirect(ib_address, 0, size);
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
} else {
|
||||
rasterizer->DispatchIndirect(ib_address, 0, size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1051,6 +1051,24 @@ struct PM4CmdDrawIndirect {
|
||||
u32 draw_initiator; ///< Draw Initiator Register
|
||||
};
|
||||
|
||||
struct PM4CmdDrawIndirectMulti {
|
||||
PM4Type3Header header; ///< header
|
||||
u32 data_offset; ///< Byte aligned offset where the required data structure starts
|
||||
union {
|
||||
u32 dw2;
|
||||
BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the
|
||||
///< BaseVertexLocation it fetched from memory
|
||||
};
|
||||
union {
|
||||
u32 dw3;
|
||||
BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the
|
||||
///< StartInstanceLocation it fetched from memory
|
||||
};
|
||||
u32 count; ///< Count of data structures to loop through before going to next packet
|
||||
u32 stride; ///< Stride in memory from one data structure to the next
|
||||
u32 draw_initiator; ///< Draw Initiator Register
|
||||
};
|
||||
|
||||
struct DrawIndexedIndirectArgs {
|
||||
u32 index_count_per_instance;
|
||||
u32 instance_count;
|
||||
|
||||
@ -5,8 +5,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/buffer_cache/region_manager.h"
|
||||
@ -71,7 +73,7 @@ public:
|
||||
// modified. If we need to flush the flush function is going to perform CPU
|
||||
// state change.
|
||||
std::scoped_lock lk{manager->lock};
|
||||
if (Config::readbacks() &&
|
||||
if (Config::getReadbacksMode() != Config::GpuReadbacksMode::Disabled &&
|
||||
manager->template IsRegionModified<Type::GPU>(offset, size)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ public:
|
||||
}
|
||||
if constexpr (type == Type::CPU) {
|
||||
UpdateProtection<!enable, false>();
|
||||
} else if (Config::readbacks()) {
|
||||
} else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precise) {
|
||||
UpdateProtection<enable, true>();
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,7 @@ public:
|
||||
bits.UnsetRange(start_page, end_page);
|
||||
if constexpr (type == Type::CPU) {
|
||||
UpdateProtection<true, false>();
|
||||
} else if (Config::readbacks()) {
|
||||
} else if (Config::getReadbacksMode() != Config::GpuReadbacksMode::Disabled) {
|
||||
UpdateProtection<false, true>();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user