Compare commits
33 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e47ebe2e9 | ||
|
|
c309a1c69b | ||
|
|
2dc0ff79ec | ||
|
|
a3b7b5b22a | ||
|
|
745d16132b | ||
|
|
3e8cd91d54 | ||
|
|
42c944b250 | ||
|
|
0de6b9e3f5 | ||
|
|
a9e4dddad5 | ||
|
|
44f616edb9 | ||
|
|
b9bba3ac89 | ||
|
|
0eacf547c0 | ||
|
|
333f792e10 | ||
|
|
569f8d3b44 | ||
|
|
f4c383392d | ||
|
|
5fcc05a4b2 | ||
|
|
b1081329b9 | ||
|
|
92d49ad652 | ||
|
|
f23a2b514b | ||
|
|
37e135d74d | ||
|
|
16fe64ad0c | ||
|
|
b6f2490288 | ||
|
|
ea716eb5cc | ||
|
|
6b898c6d69 | ||
|
|
fa5dfcb712 | ||
|
|
cb1fd1bad8 | ||
|
|
d8609eef89 | ||
|
|
f759ff3a5c | ||
|
|
55b543f466 | ||
|
|
3cce51d25b | ||
|
|
5a0d4e1d38 | ||
|
|
74671186bf | ||
|
|
ace6c2318b |
@@ -3,4 +3,4 @@
|
||||
|
||||
[codespell]
|
||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES
|
||||
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||
|
||||
@@ -74,16 +74,7 @@ android {
|
||||
|
||||
// Signed by release key, allowing for upload to Play Store.
|
||||
release {
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isMinifyEnabled = true
|
||||
isDebuggable = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
|
||||
register("relWithVersionCode") {
|
||||
resValue("string", "app_name_suffixed", "yuzu")
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isMinifyEnabled = true
|
||||
isDebuggable = false
|
||||
@@ -96,6 +87,7 @@ android {
|
||||
// builds a release build that doesn't need signing
|
||||
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
|
||||
register("relWithDebInfo") {
|
||||
resValue("string", "app_name_suffixed", "yuzu Debug Release")
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isMinifyEnabled = true
|
||||
isDebuggable = true
|
||||
@@ -103,16 +95,19 @@ android {
|
||||
getDefaultProguardFile("proguard-android.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
versionNameSuffix = "-debug"
|
||||
versionNameSuffix = "-relWithDebInfo"
|
||||
applicationIdSuffix = ".relWithDebInfo"
|
||||
isJniDebuggable = true
|
||||
}
|
||||
|
||||
// Signed by debug key disallowing distribution on Play Store.
|
||||
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
|
||||
debug {
|
||||
resValue("string", "app_name_suffixed", "yuzu Debug")
|
||||
isDebuggable = true
|
||||
isJniDebuggable = true
|
||||
versionNameSuffix = "-debug"
|
||||
applicationIdSuffix = ".debug"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,19 +157,19 @@ dependencies {
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.fragment:fragment-ktx:1.5.7")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.0")
|
||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
implementation("androidx.preference:preference:1.2.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.0.0")
|
||||
implementation("androidx.window:window:1.1.0")
|
||||
implementation("org.ini4j:ini4j:0.5.4")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
<application
|
||||
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
||||
android:label="@string/app_name"
|
||||
android:label="@string/app_name_suffixed"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:allowBackup="true"
|
||||
android:hasFragileUserData="true"
|
||||
|
||||
@@ -6,15 +6,12 @@ package org.yuzu.yuzu_emu.activities
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Rect
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
@@ -23,7 +20,6 @@ import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
@@ -39,7 +35,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
|
||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
|
||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
|
||||
import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||
@@ -148,11 +143,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
super.onResume()
|
||||
nfcReader.startScanning()
|
||||
startMotionSensorListener()
|
||||
|
||||
NativeLibrary.notifyOrientationChange(
|
||||
EmulationMenuSettings.landscapeScreenLayout,
|
||||
getAdjustedRotation()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -258,24 +248,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
|
||||
|
||||
private fun getAdjustedRotation():Int {
|
||||
val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
|
||||
val config: Configuration = resources.configuration
|
||||
|
||||
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
|
||||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0 ||
|
||||
(config.screenLayout and Configuration.SCREENLAYOUT_SIZE_SMALL) != 0) {
|
||||
return rotation
|
||||
}
|
||||
when (rotation) {
|
||||
Surface.ROTATION_0 -> return Surface.ROTATION_90
|
||||
Surface.ROTATION_90 -> return Surface.ROTATION_0
|
||||
Surface.ROTATION_180 -> return Surface.ROTATION_270
|
||||
Surface.ROTATION_270 -> return Surface.ROTATION_180
|
||||
}
|
||||
return rotation
|
||||
}
|
||||
|
||||
private fun restoreState(savedInstanceState: Bundle) {
|
||||
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
|
||||
}
|
||||
|
||||
@@ -765,18 +765,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
|
||||
// If we have API access, calculate the safe area to draw the overlay
|
||||
var cutoutLeft = 0
|
||||
var cutoutBottom = 0
|
||||
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
|
||||
if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
|
||||
insets.boundingRectTop.bottom.toFloat() else maxY
|
||||
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
|
||||
insets.boundingRectRight.left.toFloat() else maxX
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
|
||||
if (insets != null) {
|
||||
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
|
||||
insets.boundingRectTop.bottom.toFloat() else maxY
|
||||
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
|
||||
insets.boundingRectRight.left.toFloat() else maxX
|
||||
|
||||
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
|
||||
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
|
||||
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
|
||||
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
|
||||
|
||||
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
|
||||
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
|
||||
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
|
||||
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
|
||||
}
|
||||
}
|
||||
|
||||
// This makes sure that if we have an inset on one side of the screen, we mirror it on
|
||||
|
||||
@@ -284,10 +284,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
if (result == null)
|
||||
return@registerForActivityResult
|
||||
|
||||
if (!FileUtil.hasExtension(result.toString(), "keys")) {
|
||||
if (!FileUtil.hasExtension(result, "keys")) {
|
||||
MessageDialogFragment.newInstance(
|
||||
R.string.reading_keys_failure,
|
||||
R.string.install_keys_failure_extension_description
|
||||
R.string.install_prod_keys_failure_extension_description
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
return@registerForActivityResult
|
||||
}
|
||||
@@ -379,10 +379,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
if (result == null)
|
||||
return@registerForActivityResult
|
||||
|
||||
if (!FileUtil.hasExtension(result.toString(), "bin")) {
|
||||
if (!FileUtil.hasExtension(result, "bin")) {
|
||||
MessageDialogFragment.newInstance(
|
||||
R.string.reading_keys_failure,
|
||||
R.string.install_keys_failure_extension_description
|
||||
R.string.install_amiibo_keys_failure_extension_description
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
@@ -324,7 +326,25 @@ object FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
fun hasExtension(path: String, extension: String): Boolean {
|
||||
return path.substring(path.lastIndexOf(".") + 1).contains(extension)
|
||||
fun hasExtension(path: String, extension: String): Boolean =
|
||||
path.substring(path.lastIndexOf(".") + 1).contains(extension)
|
||||
|
||||
fun hasExtension(uri: Uri, extension: String): Boolean {
|
||||
val fileName: String?
|
||||
val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
|
||||
val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
cursor?.moveToFirst()
|
||||
|
||||
if (nameIndex == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
fileName = cursor.getString(nameIndex)
|
||||
cursor.close()
|
||||
|
||||
if (fileName == null) {
|
||||
return false
|
||||
}
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,14 +94,6 @@ public:
|
||||
m_native_window = native_window;
|
||||
}
|
||||
|
||||
u32 ScreenRotation() const {
|
||||
return m_screen_rotation;
|
||||
}
|
||||
|
||||
void SetScreenRotation(u32 screen_rotation) {
|
||||
m_screen_rotation = screen_rotation;
|
||||
}
|
||||
|
||||
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
||||
const std::string& custom_driver_name,
|
||||
const std::string& file_redirect_dir) {
|
||||
@@ -400,7 +392,6 @@ private:
|
||||
// Window management
|
||||
std::unique_ptr<EmuWindow_Android> m_window;
|
||||
ANativeWindow* m_native_window{};
|
||||
u32 m_screen_rotation{};
|
||||
|
||||
// Core emulation
|
||||
Core::System m_system;
|
||||
@@ -426,10 +417,6 @@ private:
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 GetAndroidScreenRotation() {
|
||||
return EmulationSession::GetInstance().ScreenRotation();
|
||||
}
|
||||
|
||||
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||
Common::Log::Initialize();
|
||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||
@@ -473,13 +460,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
|
||||
EmulationSession::GetInstance().SurfaceChanged();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jint layout_option,
|
||||
jint rotation) {
|
||||
return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_directory) {
|
||||
|
||||
@@ -65,11 +65,8 @@
|
||||
<string name="invalid_keys_file">Invalid keys file selected</string>
|
||||
<string name="install_keys_success">Keys successfully installed</string>
|
||||
<string name="reading_keys_failure">Error reading encryption keys</string>
|
||||
<string name="install_keys_failure_extension_description">
|
||||
1. Verify your keys have the .keys extension.\n\n
|
||||
2. Keys must not be stored in the Downloads folder.\n\n
|
||||
Resolve the issue(s) and try again.
|
||||
</string>
|
||||
<string name="install_prod_keys_failure_extension_description">Verify your keys file has a .keys extension and try again.</string>
|
||||
<string name="install_amiibo_keys_failure_extension_description">Verify your keys file has a .bin extension and try again.</string>
|
||||
<string name="invalid_keys_error">Invalid encryption keys</string>
|
||||
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
||||
|
||||
@@ -715,20 +715,38 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostVertexBuffers() {
|
||||
HostBindings host_bindings;
|
||||
bool any_valid{false};
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
||||
const Binding& binding = channel_state->vertex_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
|
||||
if (!flags[Dirty::VertexBuffer0 + index]) {
|
||||
continue;
|
||||
}
|
||||
flags[Dirty::VertexBuffer0 + index] = false;
|
||||
host_bindings.min_index = std::min(host_bindings.min_index, index);
|
||||
host_bindings.max_index = std::max(host_bindings.max_index, index);
|
||||
any_valid = true;
|
||||
}
|
||||
|
||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||
const u32 offset = buffer.Offset(binding.cpu_addr);
|
||||
runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride);
|
||||
if (any_valid) {
|
||||
host_bindings.max_index++;
|
||||
for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) {
|
||||
flags[Dirty::VertexBuffer0 + index] = false;
|
||||
|
||||
const Binding& binding = channel_state->vertex_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
|
||||
|
||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||
const u32 offset = buffer.Offset(binding.cpu_addr);
|
||||
|
||||
host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer));
|
||||
host_bindings.offsets.push_back(offset);
|
||||
host_bindings.sizes.push_back(binding.size);
|
||||
host_bindings.strides.push_back(stride);
|
||||
}
|
||||
runtime.BindVertexBuffers(host_bindings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,15 +900,25 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
|
||||
if (maxwell3d->regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
HostBindings host_bindings;
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
const Binding& binding = channel_state->transform_feedback_buffers[index];
|
||||
if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
|
||||
maxwell3d->regs.transform_feedback.controls[index].stride == 0) {
|
||||
break;
|
||||
}
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, size);
|
||||
|
||||
const u32 offset = buffer.Offset(binding.cpu_addr);
|
||||
runtime.BindTransformFeedbackBuffer(index, buffer, offset, size);
|
||||
host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer));
|
||||
host_bindings.offsets.push_back(offset);
|
||||
host_bindings.sizes.push_back(binding.size);
|
||||
}
|
||||
if (host_bindings.buffers.size() > 0) {
|
||||
runtime.BindTransformFeedbackBuffers(host_bindings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1616,6 +1644,8 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
bool dirty_index{false};
|
||||
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
|
||||
const auto scalar_replace = [buffer_id](Binding& binding) {
|
||||
if (binding.buffer_id == buffer_id) {
|
||||
binding.buffer_id = BufferId{};
|
||||
@@ -1624,8 +1654,19 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
const auto replace = [scalar_replace](std::span<Binding> bindings) {
|
||||
std::ranges::for_each(bindings, scalar_replace);
|
||||
};
|
||||
scalar_replace(channel_state->index_buffer);
|
||||
replace(channel_state->vertex_buffers);
|
||||
|
||||
if (channel_state->index_buffer.buffer_id == buffer_id) {
|
||||
channel_state->index_buffer.buffer_id = BufferId{};
|
||||
dirty_index = true;
|
||||
}
|
||||
|
||||
for (u32 index = 0; index < channel_state->vertex_buffers.size(); index++) {
|
||||
auto& binding = channel_state->vertex_buffers[index];
|
||||
if (binding.buffer_id == buffer_id) {
|
||||
binding.buffer_id = BufferId{};
|
||||
dirty_vertex_buffers.push_back(index);
|
||||
}
|
||||
}
|
||||
std::ranges::for_each(channel_state->uniform_buffers, replace);
|
||||
std::ranges::for_each(channel_state->storage_buffers, replace);
|
||||
replace(channel_state->transform_feedback_buffers);
|
||||
@@ -1642,20 +1683,21 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
|
||||
slot_buffers.erase(buffer_id);
|
||||
|
||||
NotifyBufferDeletion();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::NotifyBufferDeletion() {
|
||||
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
||||
channel_state->dirty_uniform_buffers.fill(~u32{0});
|
||||
channel_state->uniform_buffer_binding_sizes.fill({});
|
||||
}
|
||||
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
flags[Dirty::IndexBuffer] = true;
|
||||
flags[Dirty::VertexBuffers] = true;
|
||||
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
||||
flags[Dirty::VertexBuffer0 + index] = true;
|
||||
if (dirty_index) {
|
||||
flags[Dirty::IndexBuffer] = true;
|
||||
}
|
||||
|
||||
if (dirty_vertex_buffers.size() > 0) {
|
||||
flags[Dirty::VertexBuffers] = true;
|
||||
for (auto index : dirty_vertex_buffers) {
|
||||
flags[Dirty::VertexBuffer0 + index] = true;
|
||||
}
|
||||
}
|
||||
channel_state->has_deleted_buffers = true;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,15 @@ static constexpr Binding NULL_BINDING{
|
||||
.buffer_id = NULL_BUFFER_ID,
|
||||
};
|
||||
|
||||
struct HostBindings {
|
||||
boost::container::small_vector<void*, NUM_VERTEX_BUFFERS> buffers;
|
||||
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> offsets;
|
||||
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> sizes;
|
||||
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> strides;
|
||||
u32 min_index{NUM_VERTEX_BUFFERS};
|
||||
u32 max_index{0};
|
||||
};
|
||||
|
||||
class BufferCacheChannelInfo : public ChannelInfo {
|
||||
public:
|
||||
BufferCacheChannelInfo() = delete;
|
||||
@@ -519,8 +528,6 @@ private:
|
||||
|
||||
void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
|
||||
|
||||
void NotifyBufferDeletion();
|
||||
|
||||
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
bool is_written) const;
|
||||
|
||||
|
||||
@@ -232,6 +232,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) {
|
||||
for (u32 index = 0; index < bindings.buffers.size(); index++) {
|
||||
BindVertexBuffer(
|
||||
bindings.min_index + index, *reinterpret_cast<Buffer*>(bindings.buffers[index]),
|
||||
static_cast<u32>(bindings.offsets[index]), static_cast<u32>(bindings.sizes[index]),
|
||||
static_cast<u32>(bindings.strides[index]));
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
|
||||
u32 offset, u32 size) {
|
||||
if (use_assembly_shaders) {
|
||||
@@ -320,6 +329,15 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer,
|
||||
static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size));
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) {
|
||||
for (u32 index = 0; index < bindings.buffers.size(); index++) {
|
||||
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index,
|
||||
reinterpret_cast<Buffer*>(bindings.buffers[index])->Handle(),
|
||||
static_cast<GLintptr>(bindings.offsets[index]),
|
||||
static_cast<GLsizeiptr>(bindings.sizes[index]));
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
PixelFormat format) {
|
||||
*texture_handles++ = buffer.View(offset, size, format);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
||||
|
||||
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
||||
void BindVertexBuffers(VideoCommon::HostBindings& bindings);
|
||||
|
||||
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
||||
|
||||
@@ -99,6 +100,7 @@ public:
|
||||
bool is_written);
|
||||
|
||||
void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size);
|
||||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings);
|
||||
|
||||
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
|
||||
@@ -87,7 +87,8 @@ void ComputePipeline::Configure() {
|
||||
texture_cache.SynchronizeComputeDescriptors();
|
||||
|
||||
boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
|
||||
std::array<GLuint, MAX_TEXTURES> samplers;
|
||||
boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
|
||||
std::array<GLuint, MAX_TEXTURES> gl_samplers;
|
||||
std::array<GLuint, MAX_TEXTURES> textures;
|
||||
std::array<GLuint, MAX_IMAGES> images;
|
||||
GLsizei sampler_binding{};
|
||||
@@ -131,7 +132,6 @@ void ComputePipeline::Configure() {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views.push_back({handle.first});
|
||||
samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
@@ -142,8 +142,8 @@ void ComputePipeline::Configure() {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views.push_back({handle.first});
|
||||
|
||||
Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
|
||||
samplers[sampler_binding++] = sampler->Handle();
|
||||
VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
|
||||
samplers.push_back(sampler);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
@@ -186,10 +186,17 @@ void ComputePipeline::Configure() {
|
||||
|
||||
const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
|
||||
num_image_buffers};
|
||||
const VideoCommon::SamplerId* samplers_it{samplers.data()};
|
||||
texture_binding += num_texture_buffers;
|
||||
image_binding += num_image_buffers;
|
||||
|
||||
u32 texture_scaling_mask{};
|
||||
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
gl_samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
|
||||
@@ -198,6 +205,12 @@ void ComputePipeline::Configure() {
|
||||
texture_scaling_mask |= 1u << texture_binding;
|
||||
}
|
||||
++texture_binding;
|
||||
|
||||
const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
gl_samplers[sampler_binding++] =
|
||||
use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
|
||||
}
|
||||
}
|
||||
u32 image_scaling_mask{};
|
||||
@@ -228,7 +241,7 @@ void ComputePipeline::Configure() {
|
||||
if (texture_binding != 0) {
|
||||
ASSERT(texture_binding == sampler_binding);
|
||||
glBindTextures(0, texture_binding, textures.data());
|
||||
glBindSamplers(0, sampler_binding, samplers.data());
|
||||
glBindSamplers(0, sampler_binding, gl_samplers.data());
|
||||
}
|
||||
if (image_binding != 0) {
|
||||
glBindImageTextures(0, image_binding, images.data());
|
||||
|
||||
@@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
||||
template <typename Spec>
|
||||
void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
|
||||
std::array<GLuint, MAX_TEXTURES> samplers;
|
||||
std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
|
||||
size_t views_index{};
|
||||
GLsizei sampler_binding{};
|
||||
size_t samplers_index{};
|
||||
|
||||
texture_cache.SynchronizeGraphicsDescriptors();
|
||||
|
||||
@@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views[views_index++] = {handle.first};
|
||||
samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,8 +350,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views[views_index++] = {handle.first};
|
||||
|
||||
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
|
||||
samplers[sampler_binding++] = sampler->Handle();
|
||||
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
|
||||
samplers[samplers_index++] = sampler;
|
||||
}
|
||||
}
|
||||
if constexpr (Spec::has_images) {
|
||||
@@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
program_manager.BindSourcePrograms(source_programs);
|
||||
}
|
||||
const VideoCommon::ImageViewInOut* views_it{views.data()};
|
||||
const VideoCommon::SamplerId* samplers_it{samplers.data()};
|
||||
GLsizei texture_binding = 0;
|
||||
GLsizei image_binding = 0;
|
||||
GLsizei sampler_binding{};
|
||||
std::array<GLuint, MAX_TEXTURES> textures;
|
||||
std::array<GLuint, MAX_IMAGES> images;
|
||||
std::array<GLuint, MAX_TEXTURES> gl_samplers;
|
||||
const auto prepare_stage{[&](size_t stage) {
|
||||
buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
|
||||
buffer_cache.BindHostStageBuffers(stage);
|
||||
@@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
u32 stage_image_binding{};
|
||||
|
||||
const auto& info{stage_infos[stage]};
|
||||
if constexpr (Spec::has_texture_buffers) {
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
gl_samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
|
||||
@@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
}
|
||||
++texture_binding;
|
||||
++stage_texture_binding;
|
||||
|
||||
const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
gl_samplers[sampler_binding++] =
|
||||
use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
@@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
if (texture_binding != 0) {
|
||||
ASSERT(texture_binding == sampler_binding);
|
||||
glBindTextures(0, texture_binding, textures.data());
|
||||
glBindSamplers(0, sampler_binding, samplers.data());
|
||||
glBindSamplers(0, sampler_binding, gl_samplers.data());
|
||||
}
|
||||
if (image_binding != 0) {
|
||||
glBindImageTextures(0, image_binding, images.data());
|
||||
|
||||
@@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) {
|
||||
|
||||
UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
|
||||
|
||||
sampler.Create();
|
||||
const GLuint handle = sampler.handle;
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
|
||||
glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
|
||||
glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
|
||||
const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy);
|
||||
} else {
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
|
||||
}
|
||||
if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
|
||||
glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
|
||||
} else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
|
||||
}
|
||||
if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
|
||||
glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
|
||||
} else if (seamless == GL_FALSE) {
|
||||
// We default to false because it's more common
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
|
||||
const auto create_sampler = [&](const f32 anisotropy) {
|
||||
OGLSampler new_sampler;
|
||||
new_sampler.Create();
|
||||
const GLuint handle = new_sampler.handle;
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
|
||||
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
|
||||
glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
|
||||
glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
|
||||
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
|
||||
} else {
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
|
||||
}
|
||||
if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
|
||||
glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
|
||||
} else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
|
||||
}
|
||||
if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
|
||||
glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
|
||||
} else if (seamless == GL_FALSE) {
|
||||
// We default to false because it's more common
|
||||
LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
|
||||
}
|
||||
return new_sampler;
|
||||
};
|
||||
|
||||
sampler = create_sampler(max_anisotropy);
|
||||
|
||||
const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy);
|
||||
if (max_anisotropy > max_anisotropy_default) {
|
||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,12 +309,21 @@ class Sampler {
|
||||
public:
|
||||
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
|
||||
|
||||
GLuint Handle() const noexcept {
|
||||
[[nodiscard]] GLuint Handle() const noexcept {
|
||||
return sampler.handle;
|
||||
}
|
||||
|
||||
[[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept {
|
||||
return sampler_default_anisotropy.handle;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
||||
return static_cast<bool>(sampler_default_anisotropy.handle);
|
||||
}
|
||||
|
||||
private:
|
||||
OGLSampler sampler;
|
||||
OGLSampler sampler_default_anisotropy;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
GuestDescriptorQueue& guest_descriptor_queue,
|
||||
const Shader::Info& info, RescalingPushConstant& rescaling,
|
||||
const VkSampler*& samplers,
|
||||
const VideoCommon::SamplerId*& samplers,
|
||||
const VideoCommon::ImageViewInOut*& views) {
|
||||
const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
|
||||
const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
|
||||
@@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const VideoCommon::ImageViewId image_view_id{(views++)->id};
|
||||
const VkSampler sampler{*(samplers++)};
|
||||
const VideoCommon::SamplerId sampler_id{*(samplers++)};
|
||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view, sampler);
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||
: sampler.Handle()};
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,6 @@
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
extern u32 GetAndroidScreenRotation();
|
||||
#endif
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
@@ -78,47 +74,6 @@ struct ScreenRectVertex {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
constexpr u32 ROTATION_0 = 0;
|
||||
constexpr u32 ROTATION_90 = 1;
|
||||
constexpr u32 ROTATION_180 = 2;
|
||||
constexpr u32 ROTATION_270 = 3;
|
||||
|
||||
// clang-format off
|
||||
switch (GetAndroidScreenRotation()) {
|
||||
case ROTATION_0:
|
||||
// Desktop
|
||||
return { 2.f / width, 0.f, 0.f, 0.f,
|
||||
0.f, 2.f / height, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
-1.f, -1.f, 0.f, 1.f};
|
||||
case ROTATION_180:
|
||||
// Reverse desktop
|
||||
return {-2.f / width, 0.f, 0.f, 0.f,
|
||||
0.f, -2.f / height, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
1.f, 1.f, 0.f, 1.f};
|
||||
case ROTATION_270:
|
||||
// Reverse landscape
|
||||
return { 0.f, -2.f / width, 0.f, 0.f,
|
||||
2.f / height, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
-1.f, 1.f, 0.f, 1.f};
|
||||
case ROTATION_90:
|
||||
default:
|
||||
// Landscape
|
||||
return { 0.f, 2.f / width, 0.f, 0.f,
|
||||
-2.f / height, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
1.f, -1.f, 0.f, 1.f};
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
// clang-format off
|
||||
return { 2.f / width, 0.f, 0.f, 0.f,
|
||||
@@ -128,8 +83,6 @@ std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
|
||||
using namespace VideoCore::Surface;
|
||||
return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
|
||||
@@ -1159,7 +1112,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = GetFormat(framebuffer),
|
||||
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
|
||||
.extent =
|
||||
{
|
||||
.width = (up_scale * framebuffer.width) >> down_shift,
|
||||
@@ -1180,14 +1133,14 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
const auto create_commit = [&](vk::Image& image) {
|
||||
return memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
|
||||
};
|
||||
const auto create_image_view = [&](vk::Image& image) {
|
||||
const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
|
||||
return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = *image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = GetFormat(framebuffer),
|
||||
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
|
||||
.components =
|
||||
{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -1217,7 +1170,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
const u32 down_shift = Settings::values.resolution_info.down_shift;
|
||||
aa_image = create_image(true, up_scale, down_shift);
|
||||
aa_commit = create_commit(aa_image);
|
||||
aa_image_view = create_image_view(aa_image);
|
||||
aa_image_view = create_image_view(aa_image, true);
|
||||
VkExtent2D size{
|
||||
.width = (up_scale * framebuffer.width) >> down_shift,
|
||||
.height = (up_scale * framebuffer.height) >> down_shift,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
@@ -502,6 +501,40 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) {
|
||||
boost::container::small_vector<VkBuffer, 32> buffer_handles;
|
||||
for (u32 index = 0; index < bindings.buffers.size(); index++) {
|
||||
auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]);
|
||||
auto handle = buffer.Handle();
|
||||
if (handle == VK_NULL_HANDLE) {
|
||||
bindings.offsets[index] = 0;
|
||||
bindings.sizes[index] = VK_WHOLE_SIZE;
|
||||
if (!device.HasNullDescriptor()) {
|
||||
ReserveNullBuffer();
|
||||
handle = *null_buffer;
|
||||
}
|
||||
}
|
||||
buffer_handles.push_back(handle);
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
scheduler.Record([bindings = bindings,
|
||||
buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers2EXT(
|
||||
bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.strides.data()));
|
||||
});
|
||||
} else {
|
||||
scheduler.Record([bindings = bindings,
|
||||
buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers(
|
||||
bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset,
|
||||
u32 size) {
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
@@ -523,6 +556,25 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) {
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
// Already logged in the rasterizer
|
||||
return;
|
||||
}
|
||||
boost::container::small_vector<VkBuffer, 4> buffer_handles;
|
||||
for (u32 index = 0; index < bindings.buffers.size(); index++) {
|
||||
auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]);
|
||||
buffer_handles.push_back(buffer.Handle());
|
||||
}
|
||||
scheduler.Record(
|
||||
[bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindTransformFeedbackBuffersEXT(
|
||||
0, static_cast<u32>(buffer_handles.size()), buffer_handles.data(),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()),
|
||||
reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()));
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
if (null_buffer) {
|
||||
return;
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Vulkan {
|
||||
class Device;
|
||||
class DescriptorPool;
|
||||
class Scheduler;
|
||||
struct HostVertexBinding;
|
||||
|
||||
class BufferCacheRuntime;
|
||||
|
||||
@@ -96,8 +97,10 @@ public:
|
||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||
|
||||
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
|
||||
void BindVertexBuffers(VideoCommon::HostBindings& bindings);
|
||||
|
||||
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
||||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings);
|
||||
|
||||
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
|
||||
[[maybe_unused]] u32 binding_index, u32 size) {
|
||||
|
||||
@@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
|
||||
static constexpr size_t max_elements = 64;
|
||||
boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
|
||||
boost::container::static_vector<VkSampler, max_elements> samplers;
|
||||
boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
|
||||
|
||||
const auto& qmd{kepler_compute.launch_description};
|
||||
const auto& cbufs{qmd.const_buffer_config};
|
||||
@@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views.push_back({handle.first});
|
||||
|
||||
Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
|
||||
samplers.push_back(sampler->Handle());
|
||||
VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
|
||||
samplers.push_back(sampler);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
@@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
buffer_cache.BindHostComputeBuffers();
|
||||
|
||||
RescalingPushConstant rescaling;
|
||||
const VkSampler* samplers_it{samplers.data()};
|
||||
const VideoCommon::SamplerId* samplers_it{samplers.data()};
|
||||
const VideoCommon::ImageViewInOut* views_it{views.data()};
|
||||
PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
|
||||
views_it);
|
||||
|
||||
@@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
|
||||
template <typename Spec>
|
||||
void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
|
||||
std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers;
|
||||
std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
|
||||
size_t sampler_index{};
|
||||
size_t view_index{};
|
||||
|
||||
@@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
views[view_index++] = {handle.first};
|
||||
|
||||
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
|
||||
samplers[sampler_index++] = sampler->Handle();
|
||||
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
|
||||
samplers[sampler_index++] = sampler;
|
||||
}
|
||||
}
|
||||
if constexpr (Spec::has_images) {
|
||||
@@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
|
||||
RescalingPushConstant rescaling;
|
||||
RenderAreaPushConstant render_area;
|
||||
const VkSampler* samplers_it{samplers.data()};
|
||||
const VideoCommon::SamplerId* samplers_it{samplers.data()};
|
||||
const VideoCommon::ImageViewInOut* views_it{views.data()};
|
||||
const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
|
||||
buffer_cache.BindHostStageBuffers(stage);
|
||||
|
||||
@@ -231,7 +231,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
#ifdef ANDROID
|
||||
// On Android, do not allow surface rotation to deviate from the frontend.
|
||||
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
#else
|
||||
.preTransform = capabilities.currentTransform,
|
||||
#endif
|
||||
.compositeAlpha = alpha_flags,
|
||||
.presentMode = present_mode,
|
||||
.clipped = VK_FALSE,
|
||||
|
||||
@@ -1802,27 +1802,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||
// Some games have samplers with garbage. Sanitize them here.
|
||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
|
||||
sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = pnext,
|
||||
.flags = 0,
|
||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||
.mipLodBias = tsc.LodBias(),
|
||||
.anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||
.maxAnisotropy = max_anisotropy,
|
||||
.compareEnable = tsc.depth_compare_enabled,
|
||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
||||
.borderColor =
|
||||
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
});
|
||||
const auto create_sampler = [&](const f32 anisotropy) {
|
||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = pnext,
|
||||
.flags = 0,
|
||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||
.mipLodBias = tsc.LodBias(),
|
||||
.anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||
.maxAnisotropy = anisotropy,
|
||||
.compareEnable = tsc.depth_compare_enabled,
|
||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
||||
.borderColor =
|
||||
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
});
|
||||
};
|
||||
|
||||
sampler = create_sampler(max_anisotropy);
|
||||
|
||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||
if (max_anisotropy > max_anisotropy_default) {
|
||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
|
||||
|
||||
@@ -279,8 +279,17 @@ public:
|
||||
return *sampler;
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
||||
return *sampler_default_anisotropy;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
||||
return static_cast<bool>(sampler_default_anisotropy);
|
||||
}
|
||||
|
||||
private:
|
||||
vk::Sampler sampler;
|
||||
vk::Sampler sampler_default_anisotropy;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
|
||||
@@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
|
||||
|
||||
ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
|
||||
|
||||
bool ImageViewBase::SupportsAnisotropy() const noexcept {
|
||||
const bool has_mips = range.extent.levels > 1;
|
||||
const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray;
|
||||
if (!has_mips || !is_2d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case PixelFormat::R8_UNORM:
|
||||
case PixelFormat::R8_SNORM:
|
||||
case PixelFormat::R8_SINT:
|
||||
case PixelFormat::R8_UINT:
|
||||
case PixelFormat::BC4_UNORM:
|
||||
case PixelFormat::BC4_SNORM:
|
||||
case PixelFormat::BC5_UNORM:
|
||||
case PixelFormat::BC5_SNORM:
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
case PixelFormat::R32G32_SINT:
|
||||
case PixelFormat::R32_FLOAT:
|
||||
case PixelFormat::R16_FLOAT:
|
||||
case PixelFormat::R16_UNORM:
|
||||
case PixelFormat::R16_SNORM:
|
||||
case PixelFormat::R16_UINT:
|
||||
case PixelFormat::R16_SINT:
|
||||
case PixelFormat::R16G16_UNORM:
|
||||
case PixelFormat::R16G16_FLOAT:
|
||||
case PixelFormat::R16G16_UINT:
|
||||
case PixelFormat::R16G16_SINT:
|
||||
case PixelFormat::R16G16_SNORM:
|
||||
case PixelFormat::R8G8_UNORM:
|
||||
case PixelFormat::R8G8_SNORM:
|
||||
case PixelFormat::R8G8_SINT:
|
||||
case PixelFormat::R8G8_UINT:
|
||||
case PixelFormat::R32G32_UINT:
|
||||
case PixelFormat::R32_UINT:
|
||||
case PixelFormat::R32_SINT:
|
||||
case PixelFormat::G4R4_UNORM:
|
||||
// Depth formats
|
||||
case PixelFormat::D32_FLOAT:
|
||||
case PixelFormat::D16_UNORM:
|
||||
// Stencil formats
|
||||
case PixelFormat::S8_UINT:
|
||||
// DepthStencil formats
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
case PixelFormat::D32_FLOAT_S8_UINT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -33,6 +33,8 @@ struct ImageViewBase {
|
||||
return type == ImageViewType::Buffer;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsAnisotropy() const noexcept;
|
||||
|
||||
ImageId image_id{};
|
||||
GPUVAddr gpu_addr = 0;
|
||||
PixelFormat format{};
|
||||
|
||||
@@ -222,30 +222,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
|
||||
|
||||
template <class P>
|
||||
typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
|
||||
return &slot_samplers[GetGraphicsSamplerId(index)];
|
||||
}
|
||||
|
||||
template <class P>
|
||||
typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
|
||||
return &slot_samplers[GetComputeSamplerId(index)];
|
||||
}
|
||||
|
||||
template <class P>
|
||||
SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
|
||||
if (index > channel_state->graphics_sampler_table.Limit()) {
|
||||
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
|
||||
return &slot_samplers[NULL_SAMPLER_ID];
|
||||
return NULL_SAMPLER_ID;
|
||||
}
|
||||
const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
|
||||
SamplerId& id = channel_state->graphics_sampler_ids[index];
|
||||
if (is_new) {
|
||||
id = FindSampler(descriptor);
|
||||
}
|
||||
return &slot_samplers[id];
|
||||
return id;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
|
||||
SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
|
||||
if (index > channel_state->compute_sampler_table.Limit()) {
|
||||
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
|
||||
return &slot_samplers[NULL_SAMPLER_ID];
|
||||
return NULL_SAMPLER_ID;
|
||||
}
|
||||
const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
|
||||
SamplerId& id = channel_state->compute_sampler_ids[index];
|
||||
if (is_new) {
|
||||
id = FindSampler(descriptor);
|
||||
}
|
||||
return &slot_samplers[id];
|
||||
return id;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
|
||||
return slot_samplers[id];
|
||||
}
|
||||
|
||||
template <class P>
|
||||
typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
|
||||
return slot_samplers[id];
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -159,6 +159,18 @@ public:
|
||||
/// Get the sampler from the compute descriptor table in the specified index
|
||||
Sampler* GetComputeSampler(u32 index);
|
||||
|
||||
/// Get the sampler id from the graphics descriptor table in the specified index
|
||||
SamplerId GetGraphicsSamplerId(u32 index);
|
||||
|
||||
/// Get the sampler id from the compute descriptor table in the specified index
|
||||
SamplerId GetComputeSamplerId(u32 index);
|
||||
|
||||
/// Return a constant reference to the given sampler id
|
||||
[[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept;
|
||||
|
||||
/// Return a reference to the given sampler id
|
||||
[[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
|
||||
|
||||
/// Refresh the state for graphics image view and sampler descriptors
|
||||
void SynchronizeGraphicsDescriptors();
|
||||
|
||||
|
||||
@@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept {
|
||||
}
|
||||
|
||||
float TSCEntry::MaxAnisotropy() const noexcept {
|
||||
if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) {
|
||||
const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
|
||||
const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
|
||||
const bool is_bilinear_filter = min_filter == TextureFilter::Linear &&
|
||||
reduction_filter == SamplerReduction::WeightedAverage;
|
||||
if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods ||
|
||||
!is_bilinear_filter || depth_compare_enabled)) {
|
||||
return 1.0f;
|
||||
}
|
||||
const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();
|
||||
|
||||
@@ -760,6 +760,7 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
|
||||
ReadGlobalSetting(Settings::values.enable_compute_pipelines);
|
||||
ReadGlobalSetting(Settings::values.use_video_framerate);
|
||||
ReadGlobalSetting(Settings::values.bg_red);
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
@@ -1415,6 +1416,7 @@ void Config::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
|
||||
WriteGlobalSetting(Settings::values.enable_compute_pipelines);
|
||||
WriteGlobalSetting(Settings::values.use_video_framerate);
|
||||
WriteGlobalSetting(Settings::values.bg_red);
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
@@ -3067,7 +3067,7 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<u8, 0x1000> buffer{};
|
||||
std::vector<u8> buffer(1_MiB);
|
||||
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||
if (install_progress->wasCanceled()) {
|
||||
|
||||
Reference in New Issue
Block a user