Merge branch 'master' into vk-fixes

This commit is contained in:
Steveice10 2024-02-23 17:11:16 -08:00 committed by GitHub
commit 2fe2dd1482
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 445 additions and 185 deletions

View File

@ -12,13 +12,13 @@ jobs:
if: ${{ !github.head_ref }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Pack
run: ./.ci/source.sh
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: source
path: artifacts/
@ -37,11 +37,11 @@ jobs:
OS: linux
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
@ -53,7 +53,7 @@ jobs:
run: ./.ci/pack.sh
if: ${{ matrix.target == 'appimage' }}
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ matrix.target == 'appimage' }}
with:
name: ${{ env.OS }}-${{ env.TARGET }}
@ -70,11 +70,11 @@ jobs:
OS: macos
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
@ -87,7 +87,7 @@ jobs:
- name: Prepare outputs for caching
run: mv build/bundle $OS-$TARGET
- name: Cache outputs for universal build
uses: actions/cache/save@v3
uses: actions/cache/save@v4
with:
path: ${{ env.OS }}-${{ env.TARGET }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
@ -98,15 +98,15 @@ jobs:
OS: macos
TARGET: universal
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download x86_64 build from cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: ${{ env.OS }}-x86_64
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Download ARM64 build from cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: ${{ env.OS }}-arm64
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
@ -118,7 +118,7 @@ jobs:
- name: Pack
run: ./.ci/pack.sh
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
@ -137,11 +137,11 @@ jobs:
OS: windows
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
@ -179,7 +179,7 @@ jobs:
- name: Pack
run: ./.ci/pack.sh
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
@ -192,11 +192,11 @@ jobs:
OS: android
TARGET: universal
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
@ -228,7 +228,7 @@ jobs:
env:
UNPACKED: 1
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: src/android/app/artifacts/
@ -242,11 +242,11 @@ jobs:
OS: ios
TARGET: arm64
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-ios-${{ github.sha }}
@ -261,7 +261,7 @@ jobs:
needs: [windows, linux, macos-universal, android, source]
if: ${{ startsWith(github.ref, 'refs/tags/') }}
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
- name: Create release
uses: actions/create-release@v1
env:

View File

@ -13,7 +13,7 @@ jobs:
image: citraemu/build-environments:linux-fresh
options: -u 1001
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build

View File

@ -20,11 +20,11 @@ jobs:
if: ${{ github.event.inputs.nightly != 'false' && github.repository == 'citra-emu/citra' }}
steps:
# this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Pre-checkout
with:
submodules: false
- uses: actions/github-script@v6
- uses: actions/github-script@v7
id: check-changes
name: 'Check for new changes'
env:
@ -38,7 +38,7 @@ jobs:
return checkBaseChanges(github, context);
- run: npm install execa@5
if: ${{ steps.check-changes.outputs.result == 'true' }}
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Checkout
if: ${{ steps.check-changes.outputs.result == 'true' }}
with:
@ -46,7 +46,7 @@ jobs:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- uses: actions/github-script@v6
- uses: actions/github-script@v7
name: 'Update and tag new commits'
if: ${{ steps.check-changes.outputs.result == 'true' }}
env:
@ -62,11 +62,11 @@ jobs:
if: ${{ github.event.inputs.canary != 'false' && github.repository == 'citra-emu/citra' }}
steps:
# this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Pre-checkout
with:
submodules: false
- uses: actions/github-script@v6
- uses: actions/github-script@v7
id: check-changes
name: 'Check for new changes'
env:
@ -79,7 +79,7 @@ jobs:
return checkCanaryChanges(github, context);
- run: npm install execa@5
if: ${{ steps.check-changes.outputs.result == 'true' }}
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Checkout
if: ${{ steps.check-changes.outputs.result == 'true' }}
with:
@ -87,7 +87,7 @@ jobs:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- uses: actions/github-script@v6
- uses: actions/github-script@v7
name: 'Check and merge canary changes'
if: ${{ steps.check-changes.outputs.result == 'true' }}
env:

View File

@ -10,7 +10,7 @@ jobs:
container: citraemu/build-environments:linux-fresh
if: ${{ github.repository == 'citra-emu/citra' }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

View File

@ -294,12 +294,21 @@ endif()
add_library(httplib INTERFACE)
if(USE_SYSTEM_CPP_HTTPLIB)
find_package(CppHttp 0.14.1)
# Detect if system cpphttplib is a shared library
# this breaks building as Citra relies on functions that are moved
# into the shared object.
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
if(HTTP_LIBS)
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
else()
if(CppHttp_FOUND)
target_link_libraries(httplib INTERFACE httplib::httplib)
else()
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
endif()
endif()
else()
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
endif()

View File

@ -9,10 +9,13 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.DocumentsTree
import org.citra.citra_emu.utils.GpuDriverHelper
import org.citra.citra_emu.utils.PermissionsHandler
import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.MemoryUtil
class CitraApplication : Application() {
private fun createNotificationChannel() {
@ -53,9 +56,20 @@ class CitraApplication : Application() {
}
NativeLibrary.logDeviceInfo()
logDeviceInfo()
createNotificationChannel()
}
fun logDeviceInfo() {
Log.info("Device Manufacturer - ${Build.MANUFACTURER}")
Log.info("Device Model - ${Build.MODEL}")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
Log.info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
Log.info("SoC Model - ${Build.SOC_MODEL}")
}
Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
}
companion object {
private var application: CitraApplication? = null

View File

@ -413,12 +413,12 @@ object NativeLibrary {
}
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
Log.verbose("[NativeLibrary] Registering EmulationActivity.")
Log.debug("[NativeLibrary] Registering EmulationActivity.")
sEmulationActivity = WeakReference(emulationActivity)
}
fun clearEmulationActivity() {
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
sEmulationActivity.clear()
}

View File

@ -94,14 +94,14 @@ object DirectoryInitialization {
val dataPath = PermissionsHandler.citraDirectory
if (dataPath.toString().isNotEmpty()) {
userPath = dataPath.toString()
Log.debug("[DirectoryInitialization] User Dir: $userPath")
android.util.Log.d("[Citra Frontend]", "[DirectoryInitialization] User Dir: $userPath")
return true
}
return false
}
private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) {
Log.verbose("[DirectoryInitialization] Copying File $asset to $output")
Log.debug("[DirectoryInitialization] Copying File $asset to $output")
try {
if (!output.exists() || overwrite) {
val inputStream = context.assets.open(asset)
@ -121,7 +121,7 @@ object DirectoryInitialization {
overwrite: Boolean,
context: Context
) {
Log.verbose("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
Log.debug("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
try {
var createdFolder = false
for (file in context.assets.list(assetFolder)!!) {

View File

@ -4,34 +4,17 @@
package org.citra.citra_emu.utils
import android.util.Log
import org.citra.citra_emu.BuildConfig
/**
* Contains methods that call through to [android.util.Log], but
* with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
* levels in release builds.
*/
object Log {
// Tracks whether we should share the old log or the current log
var gameLaunched = false
private const val TAG = "Citra Frontend"
fun verbose(message: String?) {
if (BuildConfig.DEBUG) {
Log.v(TAG, message!!)
}
}
external fun debug(message: String)
fun debug(message: String?) {
if (BuildConfig.DEBUG) {
Log.d(TAG, message!!)
}
}
external fun warning(message: String)
fun info(message: String?) = Log.i(TAG, message!!)
external fun info(message: String)
fun warning(message: String?) = Log.w(TAG, message!!)
external fun error(message: String)
fun error(message: String?) = Log.e(TAG, message!!)
external fun critical(message: String)
}

View File

@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.citra.citra_emu.utils
import android.app.ActivityManager
import android.content.Context
import android.os.Build
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.R
import java.util.Locale
import kotlin.math.ceil
object MemoryUtil {
private val context get() = CitraApplication.appContext
private val Float.hundredths: String
get() = String.format(Locale.ROOT, "%.2f", this)
const val Kb: Float = 1024F
const val Mb = Kb * 1024
const val Gb = Mb * 1024
const val Tb = Gb * 1024
const val Pb = Tb * 1024
const val Eb = Pb * 1024
fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
when {
size < Kb -> {
context.getString(
R.string.memory_formatted,
size.hundredths,
context.getString(R.string.memory_byte_shorthand)
)
}
size < Mb -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
context.getString(R.string.memory_kilobyte)
)
}
size < Gb -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
context.getString(R.string.memory_megabyte)
)
}
size < Tb -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
context.getString(R.string.memory_gigabyte)
)
}
size < Pb -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
context.getString(R.string.memory_terabyte)
)
}
size < Eb -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
context.getString(R.string.memory_petabyte)
)
}
else -> {
context.getString(
R.string.memory_formatted,
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
context.getString(R.string.memory_exabyte)
)
}
}
val totalMemory: Float
get() {
val memInfo = ActivityManager.MemoryInfo()
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
getMemoryInfo(memInfo)
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
memInfo.advertisedMem.toFloat()
} else {
memInfo.totalMem.toFloat()
}
}
fun isLessThan(minimum: Int, size: Float): Boolean =
when (size) {
Kb -> totalMemory < Mb && totalMemory < minimum
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
Eb -> totalMemory / Eb < minimum
else -> totalMemory < Kb && totalMemory < minimum
}
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
// the potential error created by memInfo.totalMem
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
}

View File

@ -28,6 +28,7 @@ add_library(citra-android SHARED
ndk_motion.cpp
ndk_motion.h
system_save_game.cpp
native_log.cpp
)
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network)

View File

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/logging/log.h>
#include <jni.h>
#include "android_common/android_common.h"
extern "C" {
void Java_org_citra_citra_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_citra_citra_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_citra_citra_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_citra_citra_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_citra_citra_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
}
} // extern "C"

View File

@ -442,6 +442,17 @@
<string name="cia_install_error_encrypted">\"%s\" must be decrypted before being used with Citra.\n A real 3DS is required</string>
<string name="cia_install_error_unknown">An unknown error occurred while installing \"%s\".\n Please see the log for more details</string>
<!-- Memory Sizes -->
<string name="memory_formatted">%1$s %2$s</string>
<string name="memory_byte">Byte</string>
<string name="memory_byte_shorthand">B</string>
<string name="memory_kilobyte">KB</string>
<string name="memory_megabyte">MB</string>
<string name="memory_gigabyte">GB</string>
<string name="memory_terabyte">TB</string>
<string name="memory_petabyte">PB</string>
<string name="memory_exabyte">EB</string>
<!-- Theme Modes -->
<string name="change_theme_mode">Change Theme Mode</string>
<string name="theme_mode_follow_system">Follow System</string>

View File

@ -298,9 +298,9 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
b.buffer_id,
state.mono_or_stereo,
state.format,
true,
{}, // 0 in u32_dsp
false,
true, // from_queue
0, // play_position
false, // has_played
});
}
LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i,
@ -321,7 +321,11 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
void Source::GenerateFrame() {
current_frame.fill({});
if (state.current_buffer.empty() && !DequeueBuffer()) {
if (state.current_buffer.empty()) {
// TODO(SachinV): Should dequeue happen at the end of the frame generation?
if (DequeueBuffer()) {
return;
}
state.enabled = false;
state.buffer_update = true;
state.last_buffer_id = state.current_buffer_id;
@ -330,8 +334,6 @@ void Source::GenerateFrame() {
}
std::size_t frame_position = 0;
state.current_sample_number = state.next_sample_number;
while (frame_position < current_frame.size()) {
if (state.current_buffer.empty() && !DequeueBuffer()) {
break;
@ -358,7 +360,7 @@ void Source::GenerateFrame() {
}
// TODO(jroweboy): Keep track of frame_position independently so that it doesn't lose precision
// over time
state.next_sample_number += static_cast<u32>(frame_position * state.rate_multiplier);
state.current_sample_number += static_cast<u32>(frame_position * state.rate_multiplier);
state.filters.ProcessFrame(current_frame);
}
@ -409,7 +411,6 @@ bool Source::DequeueBuffer() {
// the first playthrough starts at play_position, loops start at the beginning of the buffer
state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
state.next_sample_number = state.current_sample_number;
state.current_buffer_physical_address = buf.physical_address;
state.current_buffer_id = buf.buffer_id;
state.last_buffer_id = 0;
@ -420,8 +421,17 @@ bool Source::DequeueBuffer() {
state.input_queue.push(buf);
}
LOG_TRACE(Audio_DSP, "source_id={} buffer_id={} from_queue={} current_buffer.size()={}",
source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size());
// Because our interpolation consumes samples instead of using an index,
// let's just consume the samples up to the current sample number.
state.current_buffer.erase(
state.current_buffer.begin(),
std::next(state.current_buffer.begin(), state.current_sample_number));
LOG_TRACE(Audio_DSP,
"source_id={} buffer_id={} from_queue={} current_buffer.size()={}, "
"buf.has_played={}, buf.play_position={}",
source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size(), buf.has_played,
buf.play_position);
return true;
}

View File

@ -87,7 +87,7 @@ private:
Format format;
bool from_queue;
u32_dsp play_position; // = 0;
u32 play_position; // = 0;
bool has_played; // = false;
private:
@ -136,7 +136,6 @@ private:
// Current buffer
u32 current_sample_number = 0;
u32 next_sample_number = 0;
PAddr current_buffer_physical_address = 0;
AudioInterp::StereoBuffer16 current_buffer = {};
@ -171,7 +170,6 @@ private:
ar& mono_or_stereo;
ar& format;
ar& current_sample_number;
ar& next_sample_number;
ar& current_buffer_physical_address;
ar& current_buffer;
ar& buffer_update;

View File

@ -54,7 +54,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 30> Config::default_hotkeys {{
const std::array<UISettings::Shortcut, 35> Config::default_hotkeys {{
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
@ -71,6 +71,11 @@ const std::array<UISettings::Shortcut, 30> Config::default_hotkeys {{
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
{QStringLiteral("Multiplayer Browse Public Game Lobby"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}},
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
@ -557,6 +562,15 @@ void Config::ReadMultiplayerValues() {
UISettings::values.game_id = ReadSetting(QStringLiteral("game_id"), 0).toULongLong();
UISettings::values.room_description =
ReadSetting(QStringLiteral("room_description"), QString{}).toString();
UISettings::values.multiplayer_filter_text =
ReadSetting(QStringLiteral("multiplayer_filter_text"), QString{}).toString();
UISettings::values.multiplayer_filter_games_owned =
ReadSetting(QStringLiteral("multiplayer_filter_games_owned"), false).toBool();
UISettings::values.multiplayer_filter_hide_empty =
ReadSetting(QStringLiteral("multiplayer_filter_hide_empty"), false).toBool();
UISettings::values.multiplayer_filter_hide_full =
ReadSetting(QStringLiteral("multiplayer_filter_hide_full"), false).toBool();
// Read ban list back
int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
UISettings::values.ban_list.first.resize(size);
@ -1074,6 +1088,15 @@ void Config::SaveMultiplayerValues() {
WriteSetting(QStringLiteral("game_id"), UISettings::values.game_id, 0);
WriteSetting(QStringLiteral("room_description"), UISettings::values.room_description,
QString{});
WriteSetting(QStringLiteral("multiplayer_filter_text"),
UISettings::values.multiplayer_filter_text, QString{});
WriteSetting(QStringLiteral("multiplayer_filter_games_owned"),
UISettings::values.multiplayer_filter_games_owned, false);
WriteSetting(QStringLiteral("multiplayer_filter_hide_empty"),
UISettings::values.multiplayer_filter_hide_empty, false);
WriteSetting(QStringLiteral("multiplayer_filter_hide_full"),
UISettings::values.multiplayer_filter_hide_full, false);
// Write ban list
qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
for (std::size_t i = 0; i < UISettings::values.ban_list.first.size(); ++i) {

View File

@ -26,7 +26,7 @@ public:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<UISettings::Shortcut, 30> default_hotkeys;
static const std::array<UISettings::Shortcut, 35> default_hotkeys;
private:
void Initialize(const std::string& config_name);

View File

@ -647,6 +647,13 @@ void GMainWindow::InitializeHotkeys() {
link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame"));
link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot"));
link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot"));
link_action_shortcut(ui->action_View_Lobby,
QStringLiteral("Multiplayer Browse Public Game Lobby"));
link_action_shortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
link_action_shortcut(ui->action_Connect_To_Room,
QStringLiteral("Multiplayer Direct Connect to Room"));
link_action_shortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
link_action_shortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
const auto add_secondary_window_hotkey = [this](QKeySequence hotkey, const char* slot) {
// This action will fire specifically when secondary_window is in focus
@ -3190,8 +3197,10 @@ int main(int argc, char* argv[]) {
QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_policy);
#ifdef __APPLE__
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
chdir(bin_path.c_str());
auto bundle_dir = FileUtil::GetBundleDirectory();
if (bundle_dir) {
FileUtil::SetCurrentDir(bundle_dir.value() + "..");
}
#endif
#ifdef ENABLE_OPENGL

View File

@ -80,9 +80,8 @@ void DirectConnectWindow::Connect() {
// Store settings
UISettings::values.nickname = ui->nickname->text();
UISettings::values.ip = ui->ip->text();
UISettings::values.port = (ui->port->isModified() && !ui->port->text().isEmpty())
? ui->port->text()
: UISettings::values.port;
UISettings::values.port =
!ui->port->text().isEmpty() ? ui->port->text() : UISettings::values.port;
// attempt to connect in a different thread
QFuture<void> f = QtConcurrent::run([&] {

View File

@ -63,10 +63,10 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
// UI Buttons
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
@ -74,6 +74,12 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
&Lobby::OnRefreshLobby);
// Load persistent filters after events are connected to make sure they apply
ui->search->setText(UISettings::values.multiplayer_filter_text);
ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned);
ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty);
ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full);
// manually start a refresh when the window is opening
// TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as
// part of the constructor, but offload the refresh until after the window shown. perhaps emit a
@ -180,6 +186,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
UISettings::values.nickname = ui->nickname->text();
UISettings::values.ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).toString();
UISettings::values.port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toString();
UISettings::values.multiplayer_filter_text = ui->search->text();
UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
}
void Lobby::ResetModel() {

View File

@ -188,13 +188,38 @@ public:
}
QVariant data(int role) const override {
if (role != Qt::DisplayRole) {
return LobbyItem::data(role);
}
switch (role) {
case Qt::DisplayRole: {
auto members = data(MemberListRole).toList();
return QStringLiteral("%1 / %2").arg(QString::number(members.size()),
data(MaxPlayerRole).toString());
}
case Qt::ForegroundRole: {
auto members = data(MemberListRole).toList();
auto max_players = data(MaxPlayerRole).toInt();
const QColor room_full_color(255, 48, 32);
const QColor room_almost_full_color(255, 140, 32);
const QColor room_has_players_color(32, 160, 32);
const QColor room_empty_color(128, 128, 128);
if (members.size() >= max_players) {
return QBrush(room_full_color);
} else if (members.size() == (max_players - 1)) {
return QBrush(room_almost_full_color);
} else if (members.size() == 0) {
return QBrush(room_empty_color);
} else if (members.size() > 0 && members.size() < (max_players - 1)) {
return QBrush(room_has_players_color);
}
// FIXME: How to return a value that tells Qt not to modify the
// text color from the default (as if Qt::ForegroundRole wasn't overridden)?
return QBrush(nullptr);
}
default:
return LobbyItem::data(role);
}
}
bool operator<(const QStandardItem& other) const override {
// sort by rooms that have the most players

View File

@ -138,6 +138,11 @@ struct Values {
QString room_description;
std::pair<std::vector<std::string>, std::vector<std::string>> ban_list;
QString multiplayer_filter_text;
bool multiplayer_filter_games_owned;
bool multiplayer_filter_hide_empty;
bool multiplayer_filter_hide_full;
// logging
Settings::Setting<bool> show_console{false, "showConsole"};
};

View File

@ -13,7 +13,6 @@
#endif
// The user data dir
#define ROOT_DIR "."
#define USERDATA_DIR "user"
#ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR

View File

@ -634,6 +634,10 @@ std::optional<std::string> GetCurrentDir() {
std::string strDir = dir;
#endif
free(dir);
if (!strDir.ends_with(DIR_SEP)) {
strDir += DIR_SEP;
}
return strDir;
} // namespace FileUtil
@ -646,17 +650,36 @@ bool SetCurrentDir(const std::string& directory) {
}
#if defined(__APPLE__)
std::string GetBundleDirectory() {
CFURLRef BundleRef;
char AppBundlePath[MAXPATHLEN];
std::optional<std::string> GetBundleDirectory() {
// Get the main bundle for the app
BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle);
CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath));
CFRelease(BundleRef);
CFRelease(BundlePath);
CFBundleRef bundle_ref = CFBundleGetMainBundle();
if (!bundle_ref) {
return {};
}
return AppBundlePath;
CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref);
if (!bundle_url_ref) {
return {};
}
SCOPE_EXIT({ CFRelease(bundle_url_ref); });
CFStringRef bundle_path_ref = CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle);
if (!bundle_path_ref) {
return {};
}
SCOPE_EXIT({ CFRelease(bundle_path_ref); });
char app_bundle_path[MAXPATHLEN];
if (!CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path,
sizeof(app_bundle_path))) {
return {};
}
std::string path_str(app_bundle_path);
if (!path_str.ends_with(DIR_SEP)) {
path_str += DIR_SEP;
}
return path_str;
}
#endif
@ -732,22 +755,6 @@ static const std::string& GetHomeDirectory() {
}
#endif
std::string GetSysDirectory() {
std::string sysDir;
#if defined(__APPLE__)
sysDir = GetBundleDirectory();
sysDir += DIR_SEP;
sysDir += SYSDATA_DIR;
#else
sysDir = SYSDATA_DIR;
#endif
sysDir += DIR_SEP;
LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir);
return sysDir;
}
namespace {
std::unordered_map<UserPath, std::string> g_paths;
std::unordered_map<UserPath, std::string> g_default_paths;
@ -777,8 +784,10 @@ void SetUserPath(const std::string& path) {
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
auto current_dir = FileUtil::GetCurrentDir();
if (current_dir.has_value() &&
FileUtil::Exists(current_dir.value() + USERDATA_DIR DIR_SEP)) {
user_path = current_dir.value() + USERDATA_DIR DIR_SEP;
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
} else {

View File

@ -193,11 +193,8 @@ void SetCurrentRomPath(const std::string& path);
// Update the Global Path with the new value
void UpdateUserPath(UserPath path, const std::string& filename);
// Returns the path to where the sys file are
[[nodiscard]] std::string GetSysDirectory();
#ifdef __APPLE__
[[nodiscard]] std::string GetBundleDirectory();
[[nodiscard]] std::optional<std::string> GetBundleDirectory();
#endif
#ifdef _WIN32

View File

@ -14,18 +14,18 @@ namespace ConfigMem {
Handler::Handler() {
std::memset(&config_mem, 0, sizeof(config_mem));
// Values extracted from firmware 11.2.0-35E
config_mem.kernel_version_min = 0x34;
// Values extracted from firmware 11.17.0-50E
config_mem.kernel_version_min = 0x3a;
config_mem.kernel_version_maj = 0x2;
config_mem.ns_tid = 0x0004013000008002;
config_mem.sys_core_ver = 0x2;
config_mem.unit_info = 0x1; // Bit 0 set for Retail
config_mem.prev_firm = 0x1;
config_mem.ctr_sdk_ver = 0x0000F297;
config_mem.firm_version_min = 0x34;
config_mem.ctr_sdk_ver = 0x0000F450;
config_mem.firm_version_min = 0x3a;
config_mem.firm_version_maj = 0x2;
config_mem.firm_sys_core_ver = 0x2;
config_mem.firm_ctr_sdk_ver = 0x0000F297;
config_mem.firm_ctr_sdk_ver = 0x0000F450;
}
ConfigMemDef& Handler::GetConfigMem() {

View File

@ -210,10 +210,10 @@ void Process::Set3dsxKernelCaps() {
};
// Similar to Rosalina, we set kernel version to a recent one.
// This is 11.2.0, to be consistent with core/hle/kernel/config_mem.cpp
// This is 11.17.0, to be consistent with core/hle/kernel/config_mem.cpp
// TODO: refactor kernel version out so it is configurable and consistent
// among all relevant places.
kernel_version = 0x234;
kernel_version = 0x23a;
}
void Process::Run(s32 main_thread_priority, u32 stack_size) {

View File

@ -373,7 +373,10 @@ ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId ap
if (active_slot == AppletSlot::Error) {
active_slot = slot;
// Wake up the application.
// APT automatically calls enable on the first registered applet.
Enable(attributes);
// Wake up the applet.
SendParameter({
.sender_id = AppletId::None,
.destination_id = app_id,
@ -398,7 +401,8 @@ Result AppletManager::Enable(AppletAttributes attributes) {
auto slot_data = GetAppletSlot(slot);
slot_data->registered = true;
if (slot_data->attributes.applet_pos == AppletPos::System &&
if (slot_data->applet_id != AppletId::None &&
slot_data->attributes.applet_pos == AppletPos::System &&
slot_data->attributes.is_home_menu) {
slot_data->attributes.raw |= attributes.raw;
LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.",
@ -786,16 +790,23 @@ Result AppletManager::PrepareToStartSystemApplet(AppletId applet_id) {
Result AppletManager::StartSystemApplet(AppletId applet_id, std::shared_ptr<Kernel::Object> object,
const std::vector<u8>& buffer) {
auto source_applet_id = AppletId::None;
auto source_applet_id = AppletId::Application;
if (last_system_launcher_slot != AppletSlot::Error) {
const auto slot_data = GetAppletSlot(last_system_launcher_slot);
source_applet_id = slot_data->applet_id;
const auto launcher_slot_data = GetAppletSlot(last_system_launcher_slot);
source_applet_id = launcher_slot_data->applet_id;
// If a system applet is launching another system applet, reset the slot to avoid conflicts.
// This is needed because system applets won't necessarily call CloseSystemApplet before
// exiting.
if (last_system_launcher_slot == AppletSlot::SystemApplet) {
slot_data->Reset();
// APT generally clears and terminates the caller of StartSystemApplet. This helps in
// situations such as a system applet launching another system applet, which would
// otherwise deadlock.
// TODO: In real APT, the check for AppletSlot::Application does not exist; there is
// TODO: something wrong with our implementation somewhere that makes this necessary.
// TODO: Otherwise, games that attempt to launch system applets will be cleared and
// TODO: emulation will crash.
if (!launcher_slot_data->registered ||
(last_system_launcher_slot != AppletSlot::Application &&
!launcher_slot_data->attributes.no_exit_on_system_applet)) {
launcher_slot_data->Reset();
// TODO: Implement launcher process termination.
}
}

View File

@ -152,6 +152,7 @@ union AppletAttributes {
u32 raw;
BitField<0, 3, AppletPos> applet_pos;
BitField<28, 1, u32> no_exit_on_system_applet;
BitField<29, 1, u32> is_home_menu;
AppletAttributes() : raw(0) {}

View File

@ -148,8 +148,6 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
// Range check table for input
if (index >= blend_func_table.size()) {
LOG_CRITICAL(Render_OpenGL, "Unknown blend factor {}", index);
UNREACHABLE();
return GL_ONE;
}

View File

@ -96,7 +96,10 @@ inline vk::BlendFactor BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
}};
const auto index = static_cast<std::size_t>(factor);
ASSERT_MSG(index < blend_func_table.size(), "Unknown blend factor {}", index);
if (index >= blend_func_table.size()) {
LOG_CRITICAL(Render_Vulkan, "Unknown blend factor {}", index);
return vk::BlendFactor::eOne;
}
return blend_func_table[index];
}

View File

@ -4,6 +4,7 @@
#include <span>
#include <boost/container/static_vector.hpp>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/settings.h"
@ -153,6 +154,12 @@ Instance::Instance(Core::TelemetrySession& telemetry, Frontend::EmuWindow& windo
physical_device = physical_devices[physical_device_index];
available_extensions = GetSupportedExtensions(physical_device);
properties = physical_device.getProperties();
if (properties.apiVersion < TargetVulkanApiVersion) {
throw std::runtime_error(fmt::format(
"Vulkan {}.{} is required, but only {}.{} is supported by device!",
VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion),
VK_VERSION_MAJOR(properties.apiVersion), VK_VERSION_MINOR(properties.apiVersion)));
}
CollectTelemetryParameters(telemetry);
CreateDevice();
@ -629,7 +636,7 @@ void Instance::CreateAllocator() {
.device = *device,
.pVulkanFunctions = &functions,
.instance = *instance,
.vulkanApiVersion = properties.apiVersion,
.vulkanApiVersion = TargetVulkanApiVersion,
};
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
@ -670,7 +677,7 @@ void Instance::CollectToolingInfo() {
if (!tooling_info) {
return;
}
const auto tools = physical_device.getToolProperties();
const auto tools = physical_device.getToolPropertiesEXT();
for (const vk::PhysicalDeviceToolProperties& tool : tools) {
const std::string_view name = tool.name;
LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);

View File

@ -17,6 +17,7 @@
#include <memory>
#include <vector>
#include <boost/container/static_vector.hpp>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/logging/log.h"
@ -291,13 +292,14 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library,
}
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
if (!VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion) {
throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!");
}
const u32 available_version = vk::enumerateInstanceVersion();
if (available_version < VK_API_VERSION_1_1) {
throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!");
const u32 available_version = VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion
? vk::enumerateInstanceVersion()
: VK_API_VERSION_1_0;
if (available_version < TargetVulkanApiVersion) {
throw std::runtime_error(fmt::format(
"Vulkan {}.{} is required, but only {}.{} is supported by instance!",
VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion),
VK_VERSION_MAJOR(available_version), VK_VERSION_MINOR(available_version)));
}
const auto extensions = GetInstanceExtensions(window_type, enable_validation);
@ -307,7 +309,7 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library,
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "Citra Vulkan",
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
.apiVersion = VK_API_VERSION_1_3,
.apiVersion = TargetVulkanApiVersion,
};
boost::container::static_vector<const char*, 2> layers;

View File

@ -19,6 +19,8 @@ enum class WindowSystemType : u8;
namespace Vulkan {
constexpr u32 TargetVulkanApiVersion = VK_API_VERSION_1_1;
using DebugCallback =
std::variant<vk::UniqueDebugUtilsMessengerEXT, vk::UniqueDebugReportCallbackEXT>;

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include "common/literals.h"
#include "common/microprofile.h"
@ -118,9 +119,9 @@ u32 UnpackDepthStencil(const VideoCore::StagingData& data, vk::Format dest) {
}
boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers(
vk::ImageAspectFlags aspect, std::span<const vk::Image> images, std::size_t num_images) {
vk::ImageAspectFlags aspect, std::span<const vk::Image> images) {
boost::container::small_vector<vk::ImageMemoryBarrier, 3> barriers;
for (std::size_t i = 0; i < num_images; i++) {
for (const vk::Image& image : images) {
barriers.push_back(vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eNone,
@ -128,7 +129,7 @@ boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers(
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = images[i],
.image = image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
@ -218,11 +219,10 @@ Handle MakeHandle(const Instance* instance, u32 width, u32 height, u32 levels, T
}
vk::UniqueFramebuffer MakeFramebuffer(vk::Device device, vk::RenderPass render_pass, u32 width,
u32 height, std::span<const vk::ImageView> attachments,
u32 num_attachments) {
u32 height, std::span<const vk::ImageView> attachments) {
const vk::FramebufferCreateInfo framebuffer_info = {
.renderPass = render_pass,
.attachmentCount = num_attachments,
.attachmentCount = static_cast<u32>(attachments.size()),
.pAttachments = attachments.data(),
.width = width,
.height = height,
@ -710,8 +710,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1,
"Image allocation parameters are invalid");
u32 num_images = 0;
std::array<vk::Image, 3> raw_images;
boost::container::static_vector<vk::Image, 3> raw_images;
vk::ImageCreateFlags flags{};
if (texture_type == VideoCore::TextureType::CubeMap) {
@ -724,18 +723,18 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
const bool need_format_list = is_mutable && instance->IsImageFormatListSupported();
handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage,
flags, traits.aspect, need_format_list, DebugName(false));
raw_images[num_images++] = handles[0].image;
raw_images.emplace_back(handles[0].image);
if (res_scale != 1) {
handles[1] =
MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format,
traits.usage, flags, traits.aspect, need_format_list, DebugName(true));
raw_images[num_images++] = handles[1].image;
raw_images.emplace_back(handles[1].image);
}
runtime->render_manager.EndRendering();
scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
const auto barriers = MakeInitBarriers(aspect, raw_images, num_images);
scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
const auto barriers = MakeInitBarriers(aspect, raw_images);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eTopOfPipe,
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
@ -753,8 +752,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
const bool has_normal = mat && mat->Map(MapType::Normal);
const vk::Format format = traits.native;
u32 num_images = 0;
std::array<vk::Image, 2> raw_images;
boost::container::static_vector<vk::Image, 2> raw_images;
vk::ImageCreateFlags flags{};
if (texture_type == VideoCore::TextureType::CubeMap) {
@ -764,23 +762,23 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
const std::string debug_name = DebugName(false, true);
handles[0] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
traits.usage, flags, traits.aspect, false, debug_name);
raw_images[num_images++] = handles[0].image;
raw_images.emplace_back(handles[0].image);
if (res_scale != 1) {
handles[1] = MakeHandle(instance, mat->width, mat->height, levels, texture_type,
vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect,
false, debug_name);
raw_images[num_images++] = handles[1].image;
raw_images.emplace_back(handles[1].image);
}
if (has_normal) {
handles[2] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
traits.usage, flags, traits.aspect, false, debug_name);
raw_images[num_images++] = handles[2].image;
raw_images.emplace_back(handles[2].image);
}
runtime->render_manager.EndRendering();
scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
const auto barriers = MakeInitBarriers(aspect, raw_images, num_images);
scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
const auto barriers = MakeInitBarriers(aspect, raw_images);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eTopOfPipe,
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
@ -817,11 +815,10 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = traits.native, params,
staging, upload](vk::CommandBuffer cmdbuf) {
u32 num_copies = 1;
std::array<vk::BufferImageCopy, 2> buffer_image_copies;
boost::container::static_vector<vk::BufferImageCopy, 2> buffer_image_copies;
const auto rect = upload.texture_rect;
buffer_image_copies[0] = vk::BufferImageCopy{
buffer_image_copies.emplace_back(vk::BufferImageCopy{
.bufferOffset = upload.buffer_offset,
.bufferRowLength = rect.GetWidth(),
.bufferImageHeight = rect.GetHeight(),
@ -833,15 +830,16 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
},
.imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0},
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1},
};
});
if (params.aspect & vk::ImageAspectFlagBits::eStencil) {
buffer_image_copies[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
vk::BufferImageCopy& stencil_copy = buffer_image_copies[1];
vk::BufferImageCopy& stencil_copy =
buffer_image_copies.emplace_back(buffer_image_copies[0]);
stencil_copy = buffer_image_copies[0];
stencil_copy.bufferOffset += UnpackDepthStencil(staging, format);
stencil_copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
num_copies++;
}
const vk::ImageMemoryBarrier read_barrier = {
@ -869,7 +867,7 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier);
cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal,
num_copies, buffer_image_copies.data());
buffer_image_copies);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags,
vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier);
@ -1077,7 +1075,7 @@ void Surface::ScaleUp(u32 new_scale) {
runtime->render_manager.EndRendering();
scheduler->Record(
[raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
const auto barriers = MakeInitBarriers(aspect, raw_images, raw_images.size());
const auto barriers = MakeInitBarriers(aspect, raw_images);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eTopOfPipe,
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
@ -1343,7 +1341,7 @@ vk::Framebuffer Surface::Framebuffer() noexcept {
runtime->render_manager.GetRenderpass(color_format, depth_format, false);
const auto attachments = std::array{ImageView()};
framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(),
GetScaledHeight(), attachments, 1);
GetScaledHeight(), attachments);
return framebuffers[index].get();
}
@ -1473,17 +1471,16 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView();
};
u32 num_attachments = 0;
std::array<vk::ImageView, 2> attachments;
boost::container::static_vector<vk::ImageView, 2> attachments;
if (color) {
prepare(0, color);
attachments[num_attachments++] = image_views[0];
attachments.emplace_back(image_views[0]);
}
if (depth) {
prepare(1, depth);
attachments[num_attachments++] = image_views[1];
attachments.emplace_back(image_views[1]);
}
const vk::Device device = runtime.GetInstance().GetDevice();
@ -1491,11 +1488,10 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
render_pass =
render_manager.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false);
framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(),
color->GetScaledHeight(), {}, 0);
color->GetScaledHeight(), {});
} else {
render_pass = render_manager.GetRenderpass(formats[0], formats[1], false);
framebuffer =
MakeFramebuffer(device, render_pass, width, height, attachments, num_attachments);
framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments);
}
}