use_docked_mode_texts_map = {
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 55cdc17c1f..79a4bf4fd2 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -246,7 +246,7 @@ add_library(shader_recompiler STATIC
)
-target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit SPIRV-Tools::SPIRV-Tools)
+target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit::sirit SPIRV-Tools::SPIRV-Tools)
if (MSVC)
target_compile_options(shader_recompiler PRIVATE
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 1e158f3759..c1fdd374ef 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -21,7 +24,7 @@ add_executable(tests
create_target_directory_groups(tests)
-target_link_libraries(tests PRIVATE common core input_common)
+target_link_libraries(tests PRIVATE common core input_common video_core)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
add_test(NAME tests COMMAND tests)
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 27c8ed9c1d..4a168241a4 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -333,7 +333,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
add_dependencies(video_core host_shaders)
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
-target_link_libraries(video_core PRIVATE sirit)
+target_link_libraries(video_core PRIVATE sirit::sirit)
# Header-only stuff needed by all dependent targets
target_link_libraries(video_core PUBLIC Vulkan::UtilityHeaders GPUOpen::VulkanMemoryAllocator)
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 6f6e0c23a8..5223afe937 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -26,7 +26,9 @@ BufferCache::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
void(slot_buffers.insert(runtime, NullBufferParams{}));
gpu_modified_ranges.Clear();
inline_buffer_id = NULL_BUFFER_ID;
-
+#ifdef YUZU_LEGACY
+ immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
+#endif
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
@@ -1378,6 +1380,10 @@ void BufferCache
::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
});
new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size);
runtime.CopyBuffer(new_buffer, overlap, copies, true);
+#ifdef YUZU_LEGACY
+ if (immediately_free)
+ runtime.Finish();
+#endif
DeleteBuffer(overlap_id, true);
}
@@ -1668,7 +1674,12 @@ void BufferCache
::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
}
Unregister(buffer_id);
- delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
+
+#ifdef YUZU_LEGACY
+ if (!do_not_mark || !immediately_free)
+#endif
+ delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
+
slot_buffers.erase(buffer_id);
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index a45e9b35f1..486d19fb79 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -154,7 +154,11 @@ template
class BufferCache : public VideoCommon::ChannelSetupCaches {
// Page size for caching purposes.
// This is unrelated to the CPU page size and it can be changed as it seems optimal.
+#ifdef YUZU_LEGACY
+ static constexpr u32 CACHING_PAGEBITS = 12;
+#else
static constexpr u32 CACHING_PAGEBITS = 16;
+#endif
static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS;
static constexpr bool IS_OPENGL = P::IS_OPENGL;
@@ -168,9 +172,14 @@ class BufferCache : public VideoCommon::ChannelSetupCaches slot_buffers;
- DelayedDestructionRing delayed_destruction_ring;
+#ifdef YUZU_LEGACY
+ static constexpr size_t TICKS_TO_DESTROY = 6;
+#else
+ static constexpr size_t TICKS_TO_DESTROY = 8;
+#endif
+ DelayedDestructionRing delayed_destruction_ring;
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
@@ -478,6 +492,9 @@ private:
u64 minimum_memory = 0;
u64 critical_memory = 0;
BufferId inline_buffer_id;
+#ifdef YUZU_LEGACY
+ bool immediately_free = false;
+#endif
std::array> CACHING_PAGEBITS)> page_table;
Common::ScratchBuffer tmp_buffer;
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 1a8a7c8dce..e2aa6c7e49 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -64,7 +64,6 @@ void MaxwellDMA::Launch() {
// TODO(Subv): Perform more research and implement all features of this engine.
const LaunchDMA& launch = regs.launch_dma;
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
- ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
if (launch.multi_line_enable) {
const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
@@ -157,7 +156,7 @@ void MaxwellDMA::Launch() {
}
void MaxwellDMA::CopyBlockLinearToPitch() {
- UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
+
u32 bytes_per_pixel = 1;
DMA::ImageOperand src_operand;
diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp
index 293bca6d79..cec5104144 100644
--- a/src/video_core/host1x/host1x.cpp
+++ b/src/video_core/host1x/host1x.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -18,9 +21,15 @@ Host1x::~Host1x() = default;
void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
switch (type) {
case ChannelType::NvDec:
+#ifdef YUZU_LEGACY
+ std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
+#endif
devices[fd] = std::make_unique(*this, fd, syncpt, frame_queue);
break;
case ChannelType::VIC:
+#ifdef YUZU_LEGACY
+ std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
+#endif
devices[fd] = std::make_unique(*this, fd, syncpt, frame_queue);
break;
default:
diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h
index 8debac93dd..4eea214ec6 100644
--- a/src/video_core/host1x/host1x.h
+++ b/src/video_core/host1x/host1x.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -201,6 +204,10 @@ private:
std::unique_ptr> allocator;
FrameQueue frame_queue;
std::unordered_map> devices;
+#ifdef YUZU_LEGACY
+ std::once_flag nvdec_first_init;
+ std::once_flag vic_first_init;
+#endif
};
} // namespace Tegra::Host1x
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index c14b44a45a..9f7b9edd5a 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -44,9 +44,13 @@ set(SHADER_FILES
pitch_unswizzle.comp
present_area.frag
present_bicubic.frag
+ present_zero_tangent.frag
+ present_bspline.frag
+ present_mitchell.frag
present_gaussian.frag
present_lanczos.frag
present_spline1.frag
+ present_mmpx.frag
queries_prefix_scan_sum.comp
queries_prefix_scan_sum_nosubgroups.comp
resolve_conditional_render.comp
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag
index a9d9d40a38..5347fd2ef7 100644
--- a/src/video_core/host_shaders/present_bicubic.frag
+++ b/src/video_core/host_shaders/present_bicubic.frag
@@ -1,56 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-
#version 460 core
-
-
layout (location = 0) in vec2 frag_tex_coord;
-
layout (location = 0) out vec4 color;
-
layout (binding = 0) uniform sampler2D color_texture;
-
-vec4 cubic(float v) {
- vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
- vec4 s = n * n * n;
- float x = s.x;
- float y = s.y - 4.0 * s.x;
- float z = s.z - 4.0 * s.y + 6.0 * s.x;
- float w = 6.0 - x - y - z;
- return vec4(x, y, z, w) * (1.0 / 6.0);
+vec4 cubic(float x) {
+ float x2 = x * x;
+ float x3 = x2 * x;
+ return vec4(1.0, x, x2, x3) * transpose(mat4x4(
+ 0.0, 2.0, 0.0, 0.0,
+ -1.0, 0.0, 1.0, 0.0,
+ 2.0, -5.0, 4.0, -1.0,
+ -1.0, 3.0, -3.0, 1.0
+ ) * (1.0 / 2.0));
}
-
-vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) {
-
- vec2 texSize = textureSize(textureSampler, 0);
- vec2 invTexSize = 1.0 / texSize;
-
- texCoords = texCoords * texSize - 0.5;
-
- vec2 fxy = fract(texCoords);
- texCoords -= fxy;
-
- vec4 xcubic = cubic(fxy.x);
- vec4 ycubic = cubic(fxy.y);
-
- vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy;
-
- vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
- vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s;
-
- offset *= invTexSize.xxyy;
-
- vec4 sample0 = texture(textureSampler, offset.xz);
- vec4 sample1 = texture(textureSampler, offset.yz);
- vec4 sample2 = texture(textureSampler, offset.xw);
- vec4 sample3 = texture(textureSampler, offset.yw);
-
- float sx = s.x / (s.x + s.y);
- float sy = s.z / (s.z + s.w);
-
- return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
+vec4 textureBicubic(sampler2D samp, vec2 uv) {
+ vec2 tex_size = vec2(textureSize(samp, 0));
+ vec2 cc_tex = uv * tex_size - 0.5f;
+ vec2 fex = cc_tex - floor(cc_tex);
+ vec4 xcubic = cubic(fex.x);
+ vec4 ycubic = cubic(fex.y);
+ vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy;
+ vec4 z = vec4(xcubic.yw, ycubic.yw);
+ vec4 s = vec4(xcubic.xz, ycubic.xz) + z;
+ vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy;
+ vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w));
+ return mix(
+ mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x),
+ mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x),
+ n.y);
}
-
void main() {
color = textureBicubic(color_texture, frag_tex_coord);
}
diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag
new file mode 100644
index 0000000000..f229de6030
--- /dev/null
+++ b/src/video_core/host_shaders/present_bspline.frag
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+#version 460 core
+layout (location = 0) in vec2 frag_tex_coord;
+layout (location = 0) out vec4 color;
+layout (binding = 0) uniform sampler2D color_texture;
+vec4 cubic(float x) {
+ float x2 = x * x;
+ float x3 = x2 * x;
+ return vec4(1.0, x, x2, x3) * transpose(mat4x4(
+ 1.0, 4.0, 1.0, 0.0,
+ -3.0, 0.0, 3.0, 0.0,
+ 3.0, -6.0, 3.0, 0.0,
+ -1.0, 3.0, -3.0, 1.0
+ ) * (1.0 / 6.0));
+}
+vec4 textureBicubic(sampler2D samp, vec2 uv) {
+ vec2 tex_size = vec2(textureSize(samp, 0));
+ vec2 cc_tex = uv * tex_size - 0.5f;
+ vec2 fex = cc_tex - floor(cc_tex);
+ vec4 xcubic = cubic(fex.x);
+ vec4 ycubic = cubic(fex.y);
+ vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy;
+ vec4 z = vec4(xcubic.yw, ycubic.yw);
+ vec4 s = vec4(xcubic.xz, ycubic.xz) + z;
+ vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy;
+ vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w));
+ return mix(
+ mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x),
+ mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x),
+ n.y);
+}
+void main() {
+ color = textureBicubic(color_texture, frag_tex_coord);
+}
diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag
new file mode 100644
index 0000000000..4ca65cd6f0
--- /dev/null
+++ b/src/video_core/host_shaders/present_mitchell.frag
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+#version 460 core
+layout (location = 0) in vec2 frag_tex_coord;
+layout (location = 0) out vec4 color;
+layout (binding = 0) uniform sampler2D color_texture;
+vec4 cubic(float x) {
+ float x2 = x * x;
+ float x3 = x2 * x;
+ return vec4(1.0, x, x2, x3) * transpose(mat4x4(
+ 1.0, 16.0, 1.0, 0.0,
+ -9.0, 0.0, 9.0, 0.0,
+ 15.0, -36.0, 27.0, -6.0,
+ -7.0, 21.0, -21.0, 7.0
+ ) * (1.0 / 18.0));
+}
+vec4 textureBicubic(sampler2D samp, vec2 uv) {
+ vec2 tex_size = vec2(textureSize(samp, 0));
+ vec2 cc_tex = uv * tex_size - 0.5f;
+ vec2 fex = cc_tex - floor(cc_tex);
+ vec4 xcubic = cubic(fex.x);
+ vec4 ycubic = cubic(fex.y);
+ vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy;
+ vec4 z = vec4(xcubic.yw, ycubic.yw);
+ vec4 s = vec4(xcubic.xz, ycubic.xz) + z;
+ vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy;
+ vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w));
+ return mix(
+ mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x),
+ mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x),
+ n.y);
+}
+void main() {
+ color = textureBicubic(color_texture, frag_tex_coord);
+}
diff --git a/src/video_core/host_shaders/present_mmpx.frag b/src/video_core/host_shaders/present_mmpx.frag
new file mode 100644
index 0000000000..6c2c05a63a
--- /dev/null
+++ b/src/video_core/host_shaders/present_mmpx.frag
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2023 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#version 460 core
+layout(location = 0) in vec2 tex_coord;
+layout(location = 0) out vec4 frag_color;
+layout(binding = 0) uniform sampler2D tex;
+
+#define src(x, y) texture(tex, coord + vec2(x, y) * 1.0 / source_size)
+
+float luma(vec4 col) {
+ return dot(col.rgb, vec3(0.2126, 0.7152, 0.0722)) * (1.0 - col.a);
+}
+
+bool same(vec4 B, vec4 A0) {
+ return all(equal(B, A0));
+}
+
+bool notsame(vec4 B, vec4 A0) {
+ return any(notEqual(B, A0));
+}
+
+bool all_eq2(vec4 B, vec4 A0, vec4 A1) {
+ return (same(B,A0) && same(B,A1));
+}
+
+bool all_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) {
+ return (same(B,A0) && same(B,A1) && same(B,A2));
+}
+
+bool all_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) {
+ return (same(B,A0) && same(B,A1) && same(B,A2) && same(B,A3));
+}
+
+bool any_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) {
+ return (same(B,A0) || same(B,A1) || same(B,A2));
+}
+
+bool none_eq2(vec4 B, vec4 A0, vec4 A1) {
+ return (notsame(B,A0) && notsame(B,A1));
+}
+
+bool none_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) {
+ return (notsame(B,A0) && notsame(B,A1) && notsame(B,A2) && notsame(B,A3));
+}
+
+void main()
+{
+ vec2 source_size = vec2(textureSize(tex, 0));
+ vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5);
+ vec2 coord = tex_coord - pos / source_size;
+
+ vec4 E = src(0.0,0.0);
+
+ vec4 A = src(-1.0,-1.0);
+ vec4 B = src(0.0,-1.0);
+ vec4 C = src(1.0,-1.0);
+
+ vec4 D = src(-1.0,0.0);
+ vec4 F = src(1.0,0.0);
+
+ vec4 G = src(-1.0,1.0);
+ vec4 H = src(0.0,1.0);
+ vec4 I = src(1.0,1.0);
+
+ vec4 J = E;
+ vec4 K = E;
+ vec4 L = E;
+ vec4 M = E;
+
+ frag_color = E;
+
+ if(same(E,A) && same(E,B) && same(E,C) && same(E,D) && same(E,F) && same(E,G) && same(E,H) && same(E,I)) return;
+
+ vec4 P = src(0.0,2.0);
+ vec4 Q = src(-2.0,0.0);
+ vec4 R = src(2.0,0.0);
+ vec4 S = src(0.0,2.0);
+
+ float Bl = luma(B);
+ float Dl = luma(D);
+ float El = luma(E);
+ float Fl = luma(F);
+ float Hl = luma(H);
+
+ if (((same(D,B) && notsame(D,H) && notsame(D,F))) && ((El>=Dl) || same(E,A)) && any_eq3(E,A,C,G) && ((El=Bl) || same(E,C)) && any_eq3(E,A,C,I) && ((El=Hl) || same(E,G)) && any_eq3(E,A,G,I) && ((El=Fl) || same(E,I)) && any_eq3(E,C,G,I) && ((El MakeBicubic(const Device& device) {
HostShaders::PRESENT_BICUBIC_FRAG);
}
+std::unique_ptr MakeMitchell(const Device& device) {
+ return std::make_unique(device, CreateBilinearSampler(),
+ HostShaders::PRESENT_MITCHELL_FRAG);
+}
+
+std::unique_ptr MakeZeroTangent(const Device& device) {
+ return std::make_unique(device, CreateBilinearSampler(),
+ HostShaders::PRESENT_ZERO_TANGENT_FRAG);
+}
+
+std::unique_ptr MakeBSpline(const Device& device) {
+ return std::make_unique(device, CreateBilinearSampler(),
+ HostShaders::PRESENT_BSPLINE_FRAG);
+}
+
std::unique_ptr MakeGaussian(const Device& device) {
return std::make_unique(device, CreateBilinearSampler(),
HostShaders::PRESENT_GAUSSIAN_FRAG);
@@ -60,4 +79,9 @@ std::unique_ptr MakeArea(const Device& device) {
HostShaders::PRESENT_AREA_FRAG);
}
+std::unique_ptr MakeMmpx(const Device& device) {
+ return std::make_unique(device, CreateNearestNeighborSampler(),
+ HostShaders::PRESENT_MMPX_FRAG);
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h
index 7b38ac56bc..187d0f1298 100644
--- a/src/video_core/renderer_opengl/present/filters.h
+++ b/src/video_core/renderer_opengl/present/filters.h
@@ -17,10 +17,14 @@ namespace OpenGL {
std::unique_ptr MakeNearestNeighbor(const Device& device);
std::unique_ptr MakeBilinear(const Device& device);
std::unique_ptr MakeBicubic(const Device& device);
+std::unique_ptr MakeZeroTangent(const Device& device);
+std::unique_ptr MakeMitchell(const Device& device);
+std::unique_ptr MakeBSpline(const Device& device);
std::unique_ptr MakeGaussian(const Device& device);
std::unique_ptr MakeSpline1(const Device& device);
std::unique_ptr MakeLanczos(const Device& device);
std::unique_ptr MakeScaleForce(const Device& device);
std::unique_ptr MakeArea(const Device& device);
+std::unique_ptr MakeMmpx(const Device& device);
} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 7bfcd6503b..68543bdd48 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -46,6 +46,38 @@ namespace Vulkan {
using VideoCommon::ImageViewType;
namespace {
+
+[[nodiscard]] VkImageAspectFlags AspectMaskFromFormat(VideoCore::Surface::PixelFormat format) {
+ using VideoCore::Surface::SurfaceType;
+ switch (VideoCore::Surface::GetFormatType(format)) {
+ case SurfaceType::ColorTexture:
+ return VK_IMAGE_ASPECT_COLOR_BIT;
+ case SurfaceType::Depth:
+ return VK_IMAGE_ASPECT_DEPTH_BIT;
+ case SurfaceType::Stencil:
+ return VK_IMAGE_ASPECT_STENCIL_BIT;
+ case SurfaceType::DepthStencil:
+ return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ default:
+ return VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+}
+
+[[nodiscard]] VkImageSubresourceRange SubresourceRangeFromView(const ImageView& image_view) {
+ auto range = image_view.range;
+ if ((image_view.flags & VideoCommon::ImageViewFlagBits::Slice) != VideoCommon::ImageViewFlagBits{}) {
+ range.base.layer = 0;
+ range.extent.layers = 1;
+ }
+ return VkImageSubresourceRange{
+ .aspectMask = AspectMaskFromFormat(image_view.format),
+ .baseMipLevel = static_cast(range.base.level),
+ .levelCount = static_cast(range.extent.levels),
+ .baseArrayLayer = static_cast(range.base.layer),
+ .layerCount = static_cast(range.extent.layers),
+ };
+}
+
struct PushConstants {
std::array tex_scale;
std::array tex_offset;
@@ -417,6 +449,40 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
0, barrier);
}
+void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) {
+ const VkImage image = image_view.ImageHandle();
+ const VkImageSubresourceRange subresource_range = SubresourceRangeFromView(image_view);
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([image, subresource_range](vk::CommandBuffer cmdbuf) {
+ const VkImageMemoryBarrier barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_SHADER_WRITE_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = image,
+ .subresourceRange = subresource_range,
+ };
+ cmdbuf.PipelineBarrier(
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT |
+ VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
+ VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ 0,
+ barrier);
+ });
+}
+
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
const VkRenderPass render_pass = framebuffer->RenderPass();
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
@@ -484,7 +550,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
BlitImageHelper::~BlitImageHelper() = default;
-void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_view,
+void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
@@ -496,10 +562,12 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
+ const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
+
+ RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
src_view](vk::CommandBuffer cmdbuf) {
- // TODO: Barriers
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
@@ -538,7 +606,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
}
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
- VkImageView src_depth_view, VkImageView src_stencil_view,
+ ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
@@ -554,10 +622,13 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
+ const VkImageView src_depth_view = src_image_view.DepthView();
+ const VkImageView src_stencil_view = src_image_view.StencilView();
+
+ RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view,
src_stencil_view, this](vk::CommandBuffer cmdbuf) {
- // TODO: Barriers
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
src_stencil_view);
@@ -692,6 +763,7 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent = GetConversionExtent(src_image_view);
+ RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
@@ -717,7 +789,6 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
- // TODO: Barriers
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
@@ -737,6 +808,7 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent = GetConversionExtent(src_image_view);
+ RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
this](vk::CommandBuffer cmdbuf) {
@@ -763,7 +835,6 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
src_stencil_view);
- // TODO: Barriers
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 3d400be6a9..bdb8cce883 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -1,4 +1,7 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -43,7 +46,7 @@ public:
StateTracker& state_tracker, DescriptorPool& descriptor_pool);
~BlitImageHelper();
- void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
+ void BlitColor(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
@@ -52,9 +55,9 @@ public:
VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
const Region2D& src_region, const Extent3D& src_size);
- void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view,
- VkImageView src_stencil_view, const Region2D& dst_region,
- const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
+ void BlitDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region,
+ Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp
index e0f2b26f84..0a28ea6349 100644
--- a/src/video_core/renderer_vulkan/present/filters.cpp
+++ b/src/video_core/renderer_vulkan/present/filters.cpp
@@ -7,6 +7,8 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
+#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/host_shaders/present_area_frag_spv.h"
@@ -14,6 +16,10 @@
#include "video_core/host_shaders/present_gaussian_frag_spv.h"
#include "video_core/host_shaders/present_lanczos_frag_spv.h"
#include "video_core/host_shaders/present_spline1_frag_spv.h"
+#include "video_core/host_shaders/present_mitchell_frag_spv.h"
+#include "video_core/host_shaders/present_bspline_frag_spv.h"
+#include "video_core/host_shaders/present_zero_tangent_frag_spv.h"
+#include "video_core/host_shaders/present_mmpx_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
@@ -52,13 +58,28 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram
BuildShader(device, PRESENT_SPLINE1_FRAG_SPV));
}
-std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) {
+std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) {
// No need for handrolled shader -- if the VK impl can do it for us ;)
- if (device.IsExtFilterCubicSupported())
- return std::make_unique(device, frame_format, CreateCubicSampler(device),
- BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
- return std::make_unique(device, frame_format, CreateBilinearSampler(device),
- BuildShader(device, PRESENT_BICUBIC_FRAG_SPV));
+ // Catmull-Rom is default bicubic for all implementations...
+ if (device.IsExtFilterCubicSupported() && (device.IsQcomFilterCubicWeightsSupported() || qcom_weights == VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM)) {
+ return std::make_unique(device, frame_format, CreateCubicSampler(device,
+ qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
+ } else {
+ return std::make_unique(device, frame_format, CreateBilinearSampler(device), [&](){
+ switch (qcom_weights) {
+ case VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM:
+ return BuildShader(device, PRESENT_BICUBIC_FRAG_SPV);
+ case VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM:
+ return BuildShader(device, PRESENT_ZERO_TANGENT_FRAG_SPV);
+ case VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM:
+ return BuildShader(device, PRESENT_BSPLINE_FRAG_SPV);
+ case VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM:
+ return BuildShader(device, PRESENT_MITCHELL_FRAG_SPV);
+ default:
+ UNREACHABLE();
+ }
+ }());
+ }
}
std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format) {
@@ -81,4 +102,9 @@ std::unique_ptr MakeArea(const Device& device, VkFormat frame_f
BuildShader(device, PRESENT_AREA_FRAG_SPV));
}
+std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format) {
+ return std::make_unique(device, frame_format, CreateNearestNeighborSampler(device),
+ BuildShader(device, PRESENT_MMPX_FRAG_SPV));
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h
index 015bffc8a5..afc3ba29a0 100644
--- a/src/video_core/renderer_vulkan/present/filters.h
+++ b/src/video_core/renderer_vulkan/present/filters.h
@@ -17,11 +17,12 @@ class MemoryAllocator;
std::unique_ptr MakeNearestNeighbor(const Device& device, VkFormat frame_format);
std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format);
-std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format);
+std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights);
std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format);
std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format);
std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format);
std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format);
std::unique_ptr MakeArea(const Device& device, VkFormat frame_format);
+std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format);
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp
index 5676dfe62a..fee19a69c2 100644
--- a/src/video_core/renderer_vulkan/present/layer.cpp
+++ b/src/video_core/renderer_vulkan/present/layer.cpp
@@ -332,7 +332,7 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
read_barrier);
cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index 0b1a89eec0..29a1c34976 100644
--- a/src/video_core/renderer_vulkan/present/util.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -624,8 +624,8 @@ vk::Sampler CreateNearestNeighborSampler(const Device& device) {
return device.GetLogical().CreateSampler(ci_nn);
}
-vk::Sampler CreateCubicSampler(const Device& device) {
- const VkSamplerCreateInfo ci_nn{
+vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights) {
+ VkSamplerCreateInfo ci_nn{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -645,7 +645,14 @@ vk::Sampler CreateCubicSampler(const Device& device) {
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
-
+ const VkSamplerCubicWeightsCreateInfoQCOM ci_qcom_nn{
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUBIC_WEIGHTS_CREATE_INFO_QCOM,
+ .pNext = nullptr,
+ .cubicWeights = qcom_weights
+ };
+ // If not specified, assume Catmull-Rom
+ if (qcom_weights != VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM)
+ ci_nn.pNext = &ci_qcom_nn;
return device.GetLogical().CreateSampler(ci_nn);
}
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h
index 11810352df..38cc6203c5 100644
--- a/src/video_core/renderer_vulkan/present/util.h
+++ b/src/video_core/renderer_vulkan/present/util.h
@@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector
VkDescriptorSet set, u32 binding);
vk::Sampler CreateBilinearSampler(const Device& device);
vk::Sampler CreateNearestNeighborSampler(const Device& device);
-vk::Sampler CreateCubicSampler(const Device& device);
+vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights);
void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
VkExtent2D extent);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 6f3a0e4cd1..e6e72cdca7 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -164,15 +164,6 @@ try
PresentFiltersForAppletCapture)
, rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, scheduler) {
- // Initialize RAII wrappers after creating the main objects
- if (Settings::values.enable_raii.GetValue()) {
- managed_instance = MakeManagedInstance(instance, dld);
- if (Settings::values.renderer_debug) {
- managed_debug_messenger = MakeManagedDebugUtilsMessenger(debug_messenger, instance, dld);
- }
- managed_surface = MakeManagedSurface(surface, instance, dld);
- }
-
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
turbo_mode.emplace(instance, dld);
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index c1e6d5db7f..4fb88b29de 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,7 +23,6 @@
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
-#include "video_core/vulkan_common/vulkan_raii.h"
namespace Core::Memory {
class Memory;
@@ -78,16 +80,10 @@ private:
// Keep original handles for compatibility with existing code
vk::Instance instance;
- // RAII wrapper for instance
- ManagedInstance managed_instance;
vk::DebugUtilsMessenger debug_messenger;
- // RAII wrapper for debug messenger
- ManagedDebugUtilsMessenger managed_debug_messenger;
vk::SurfaceKHR surface;
- // RAII wrapper for surface
- ManagedSurface managed_surface;
Device device;
MemoryAllocator memory_allocator;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index b720bcded3..0f54dd5ade 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -7,6 +7,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include "video_core/framebuffer_config.h"
#include "video_core/present.h"
#include "video_core/renderer_vulkan/present/filters.h"
@@ -41,7 +42,16 @@ void BlitScreen::SetWindowAdaptPass() {
window_adapt = MakeNearestNeighbor(device, swapchain_view_format);
break;
case Settings::ScalingFilter::Bicubic:
- window_adapt = MakeBicubic(device, swapchain_view_format);
+ window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM);
+ break;
+ case Settings::ScalingFilter::ZeroTangent:
+ window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM);
+ break;
+ case Settings::ScalingFilter::BSpline:
+ window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM);
+ break;
+ case Settings::ScalingFilter::Mitchell:
+ window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM);
break;
case Settings::ScalingFilter::Spline1:
window_adapt = MakeSpline1(device, swapchain_view_format);
@@ -58,6 +68,9 @@ void BlitScreen::SetWindowAdaptPass() {
case Settings::ScalingFilter::Area:
window_adapt = MakeArea(device, swapchain_view_format);
break;
+ case Settings::ScalingFilter::Mmpx:
+ window_adapt = MakeMmpx(device, swapchain_view_format);
+ break;
case Settings::ScalingFilter::Fsr:
case Settings::ScalingFilter::Bilinear:
default:
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 0b29ad1389..161f6c8b9f 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -412,7 +412,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
@@ -460,7 +460,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) {
MakeImageCopy(frame->width, frame->height, extent.width, extent.height));
}
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {},
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {},
{}, {}, post_barriers);
cmdbuf.End();
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 3b35e28c05..fdd2de2379 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -351,6 +351,7 @@ void Swapchain::CreateSemaphores() {
void Swapchain::Destroy() {
frame_index = 0;
present_semaphores.clear();
+ render_semaphores.clear();
swapchain.reset();
}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8d1d609a35..50a73ea76d 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1068,7 +1068,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, READ_BARRIER, {}, middle_out_barrier);
- cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies);
+ cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk_out_copies);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0, {}, {}, post_barriers);
});
@@ -1086,8 +1086,8 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
return;
}
if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT && !is_src_msaa && !is_dst_msaa) {
- blit_image_helper.BlitColor(dst_framebuffer, src.Handle(Shader::TextureType::Color2D),
- dst_region, src_region, filter, operation);
+ blit_image_helper.BlitColor(dst_framebuffer, src, dst_region, src_region, filter,
+ operation);
return;
}
ASSERT(src.format == dst.format);
@@ -1106,8 +1106,8 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
}();
if (!can_blit_depth_stencil) {
UNIMPLEMENTED_IF(is_src_msaa || is_dst_msaa);
- blit_image_helper.BlitDepthStencil(dst_framebuffer, src.DepthView(), src.StencilView(),
- dst_region, src_region, filter, operation);
+ blit_image_helper.BlitDepthStencil(dst_framebuffer, src, dst_region, src_region,
+ filter, operation);
return;
}
}
@@ -1968,18 +1968,17 @@ bool Image::BlitScaleHelper(bool scale_up) {
blit_framebuffer =
std::make_unique(*runtime, view_ptr, nullptr, extent, scale_up);
}
- const auto color_view = blit_view->Handle(Shader::TextureType::Color2D);
- runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region,
+ runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), *blit_view, dst_region,
src_region, operation, BLIT_OPERATION);
} else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
if (!blit_framebuffer) {
blit_framebuffer =
std::make_unique(*runtime, nullptr, view_ptr, extent, scale_up);
}
- runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), blit_view->DepthView(),
- blit_view->StencilView(), dst_region,
- src_region, operation, BLIT_OPERATION);
+ runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), *blit_view,
+ dst_region, src_region, operation,
+ BLIT_OPERATION);
} else {
// TODO: Use helper blits where applicable
flags &= ~ImageFlagBits::Rescaled;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index f7d22afde2..01a9a6a3f1 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -110,7 +110,12 @@ class TextureCache : public VideoCommon::ChannelSetupCaches::max)()};
+#ifdef YUZU_LEGACY
+ static constexpr s64 TARGET_THRESHOLD = 3_GiB;
+#else
static constexpr s64 TARGET_THRESHOLD = 4_GiB;
+#endif
+
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
@@ -479,7 +484,11 @@ private:
};
Common::LeastRecentlyUsedCache lru_cache;
+ #ifdef YUZU_LEGACY
+ static constexpr size_t TICKS_TO_DESTROY = 6;
+ #else
static constexpr size_t TICKS_TO_DESTROY = 8;
+#endif
DelayedDestructionRing sentenced_images;
DelayedDestructionRing sentenced_image_view;
DelayedDestructionRing sentenced_framebuffers;
diff --git a/src/video_core/vulkan_common/vma.h b/src/video_core/vulkan_common/vma.h
index 911c1114b2..e022b2bf7d 100644
--- a/src/video_core/vulkan_common/vma.h
+++ b/src/video_core/vulkan_common/vma.h
@@ -10,4 +10,12 @@
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4189 )
+#endif
#include "vk_mem_alloc.h"
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index bd54144480..cb13f28523 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -89,7 +89,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \
EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \
- EXTENSION(EXT, FILTER_CUBIC, filter_cubic)
+ EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \
+ EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
// Define extensions which must be supported.
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
@@ -558,6 +559,11 @@ public:
return extensions.filter_cubic;
}
+ /// Returns true if the device supports VK_QCOM_filter_cubic_weights
+ bool IsQcomFilterCubicWeightsSupported() const {
+ return extensions.filter_cubic_weights;
+ }
+
/// Returns true if the device supports VK_EXT_line_rasterization.
bool IsExtLineRasterizationSupported() const {
return extensions.line_rasterization;
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index f0309117bf..4cd3442d97 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -325,4 +325,6 @@ namespace Vulkan {
return MemoryCommit(allocator, a, info);
}
+
+
} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_raii.h b/src/video_core/vulkan_common/vulkan_raii.h
deleted file mode 100644
index cf5e268b68..0000000000
--- a/src/video_core/vulkan_common/vulkan_raii.h
+++ /dev/null
@@ -1,231 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2025 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-
-#include "common/logging/log.h"
-
-#include "video_core/vulkan_common/vulkan_wrapper.h"
-
-namespace Vulkan {
-
-/**
- * RAII wrapper for Vulkan resources.
- * Automatically manages the lifetime of Vulkan objects using RAII principles.
- */
-template
-class VulkanRaii {
-public:
- using DeleterFunc = std::function;
-
- // Default constructor - creates a null handle
- VulkanRaii() : handle{}, deleter{}, dispatch{} {}
-
- // Constructor with handle and deleter
- VulkanRaii(T handle_, DeleterFunc deleter_, const Dispatch& dispatch_, const char* resource_name = "Vulkan resource")
- : handle{handle_}, deleter{std::move(deleter_)}, dispatch{dispatch_} {
- LOG_DEBUG(Render_Vulkan, "RAII wrapper created for {}", resource_name);
- }
-
- // Move constructor
- VulkanRaii(VulkanRaii&& other) noexcept
- : handle{std::exchange(other.handle, VK_NULL_HANDLE)},
- deleter{std::move(other.deleter)},
- dispatch{other.dispatch} {
- }
-
- // Move assignment
- VulkanRaii& operator=(VulkanRaii&& other) noexcept {
- if (this != &other) {
- cleanup();
- handle = std::exchange(other.handle, VK_NULL_HANDLE);
- deleter = std::move(other.deleter);
- dispatch = other.dispatch;
- }
- return *this;
- }
-
- // Destructor - automatically cleans up the resource
- ~VulkanRaii() {
- cleanup();
- }
-
- // Disallow copying
- VulkanRaii(const VulkanRaii&) = delete;
- VulkanRaii& operator=(const VulkanRaii&) = delete;
-
- // Get the underlying handle
- T get() const noexcept {
- return handle;
- }
-
- // Check if the handle is valid
- bool valid() const noexcept {
- return handle != VK_NULL_HANDLE;
- }
-
- // Release ownership of the handle without destroying it
- T release() noexcept {
- return std::exchange(handle, VK_NULL_HANDLE);
- }
-
- // Reset the handle (destroying the current one if it exists)
- void reset(T new_handle = VK_NULL_HANDLE, DeleterFunc new_deleter = {}) {
- cleanup();
- handle = new_handle;
- deleter = std::move(new_deleter);
- }
-
- // Implicit conversion to handle type
- operator T() const noexcept {
- return handle;
- }
-
- // Dereference operator for pointer-like access
- T operator->() const noexcept {
- return handle;
- }
-
-private:
- // Optimized cleanup function
- void cleanup() noexcept {
- if (handle != VK_NULL_HANDLE && deleter) {
- deleter(handle, dispatch);
- handle = VK_NULL_HANDLE;
- }
- }
-
- T handle;
- DeleterFunc deleter;
- Dispatch dispatch;
-};
-
-// Common type aliases for Vulkan RAII wrappers with clearer names
-using ManagedInstance = VulkanRaii;
-using ManagedDevice = VulkanRaii;
-using ManagedSurface = VulkanRaii;
-using ManagedSwapchain = VulkanRaii;
-using ManagedCommandPool = VulkanRaii;
-using ManagedBuffer = VulkanRaii;
-using ManagedImage = VulkanRaii;
-using ManagedImageView = VulkanRaii;
-using ManagedSampler = VulkanRaii;
-using ManagedShaderModule = VulkanRaii;
-using ManagedPipeline = VulkanRaii;
-using ManagedPipelineLayout = VulkanRaii;
-using ManagedDescriptorSetLayout = VulkanRaii;
-using ManagedDescriptorPool = VulkanRaii;
-using ManagedSemaphore = VulkanRaii;
-using ManagedFence = VulkanRaii;
-using ManagedDebugUtilsMessenger = VulkanRaii;
-
-// Helper functions to create RAII wrappers
-
-/**
- * Creates an RAII wrapper for a Vulkan instance
- */
-inline ManagedInstance MakeManagedInstance(const vk::Instance& instance, const vk::InstanceDispatch& dispatch) {
- auto deleter = [](VkInstance handle, const vk::InstanceDispatch& dld) {
- dld.vkDestroyInstance(handle, nullptr);
- };
- return ManagedInstance(*instance, deleter, dispatch, "VkInstance");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan device
- */
-inline ManagedDevice MakeManagedDevice(const vk::Device& device, const vk::DeviceDispatch& dispatch) {
- auto deleter = [](VkDevice handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroyDevice(handle, nullptr);
- };
- return ManagedDevice(*device, deleter, dispatch, "VkDevice");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan surface
- */
-inline ManagedSurface MakeManagedSurface(const vk::SurfaceKHR& surface, const vk::Instance& instance, const vk::InstanceDispatch& dispatch) {
- auto deleter = [instance_ptr = *instance](VkSurfaceKHR handle, const vk::InstanceDispatch& dld) {
- dld.vkDestroySurfaceKHR(instance_ptr, handle, nullptr);
- };
- return ManagedSurface(*surface, deleter, dispatch, "VkSurfaceKHR");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan debug messenger
- */
-inline ManagedDebugUtilsMessenger MakeManagedDebugUtilsMessenger(const vk::DebugUtilsMessenger& messenger,
- const vk::Instance& instance,
- const vk::InstanceDispatch& dispatch) {
- auto deleter = [instance_ptr = *instance](VkDebugUtilsMessengerEXT handle, const vk::InstanceDispatch& dld) {
- dld.vkDestroyDebugUtilsMessengerEXT(instance_ptr, handle, nullptr);
- };
- return ManagedDebugUtilsMessenger(*messenger, deleter, dispatch, "VkDebugUtilsMessengerEXT");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan swapchain
- */
-inline ManagedSwapchain MakeManagedSwapchain(VkSwapchainKHR swapchain_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkSwapchainKHR handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroySwapchainKHR(device_handle, handle, nullptr);
- };
- return ManagedSwapchain(swapchain_handle, deleter, dispatch, "VkSwapchainKHR");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan buffer
- */
-inline ManagedBuffer MakeManagedBuffer(VkBuffer buffer_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkBuffer handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroyBuffer(device_handle, handle, nullptr);
- };
- return ManagedBuffer(buffer_handle, deleter, dispatch, "VkBuffer");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan image
- */
-inline ManagedImage MakeManagedImage(VkImage image_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkImage handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroyImage(device_handle, handle, nullptr);
- };
- return ManagedImage(image_handle, deleter, dispatch, "VkImage");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan image view
- */
-inline ManagedImageView MakeManagedImageView(VkImageView view_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkImageView handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroyImageView(device_handle, handle, nullptr);
- };
- return ManagedImageView(view_handle, deleter, dispatch, "VkImageView");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan semaphore
- */
-inline ManagedSemaphore MakeManagedSemaphore(VkSemaphore semaphore_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkSemaphore handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroySemaphore(device_handle, handle, nullptr);
- };
- return ManagedSemaphore(semaphore_handle, deleter, dispatch, "VkSemaphore");
-}
-
-/**
- * Creates an RAII wrapper for a Vulkan fence
- */
-inline ManagedFence MakeManagedFence(VkFence fence_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
- auto deleter = [device_handle](VkFence handle, const vk::DeviceDispatch& dld) {
- dld.vkDestroyFence(device_handle, handle, nullptr);
- };
- return ManagedFence(fence_handle, deleter, dispatch, "VkFence");
-}
-
-} // namespace Vulkan
\ No newline at end of file
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 949b91499d..b77d01711a 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -12,7 +12,6 @@
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "common/settings.h"
#include "video_core/vulkan_common/vk_enum_string_helper.h"
#include "video_core/vulkan_common/vma.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -311,10 +310,7 @@ const char* Exception::what() const noexcept {
}
void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept {
- // FIXME: A double free occurs here if RAII is enabled.
- if (!Settings::values.enable_raii.GetValue()) {
- dld.vkDestroyInstance(instance, nullptr);
- }
+ dld.vkDestroyInstance(instance, nullptr);
}
void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept {
@@ -417,10 +413,7 @@ void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle,
}
void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
- // FIXME: A double free occurs here if RAII is enabled.
- if (!Settings::values.enable_raii.GetValue()) {
- dld.vkDestroySurfaceKHR(instance, handle, nullptr);
- }
+ dld.vkDestroySurfaceKHR(instance, handle, nullptr);
}
VkResult Free(VkDevice device, VkDescriptorPool handle, Span sets,
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 6501094f05..39396b3279 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -516,7 +516,7 @@ public:
}
/// Returns true when there's a held object.
- operator bool() const noexcept {
+ explicit operator bool() const noexcept {
return handle != nullptr;
}
@@ -627,7 +627,7 @@ class Instance : public Handle {
public:
/// Creates a Vulkan instance.
/// @throw Exception on initialization error.
- static Instance Create(u32 version, Span layers, Span extensions,
+ [[nodiscard]] static Instance Create(u32 version, Span layers, Span extensions,
InstanceDispatch& dispatch);
/// Enumerates physical devices.
@@ -637,12 +637,12 @@ public:
/// Creates a debug callback messenger.
/// @throw Exception on creation failure.
- DebugUtilsMessenger CreateDebugUtilsMessenger(
+ [[nodiscard]] DebugUtilsMessenger CreateDebugUtilsMessenger(
const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
/// Creates a debug report callback.
/// @throw Exception on creation failure.
- DebugReportCallback CreateDebugReportCallback(
+ [[nodiscard]] DebugReportCallback CreateDebugReportCallback(
const VkDebugReportCallbackCreateInfoEXT& create_info) const;
/// Returns dispatch table.
@@ -986,58 +986,60 @@ class Device : public Handle {
using Handle::Handle;
public:
- static Device Create(VkPhysicalDevice physical_device, Span queues_ci,
- Span enabled_extensions, const void* next,
- DeviceDispatch& dispatch);
+ [[nodiscard]] static Device Create(VkPhysicalDevice physical_device,
+ Span queues_ci,
+ Span enabled_extensions, const void* next,
+ DeviceDispatch& dispatch);
- Queue GetQueue(u32 family_index) const noexcept;
+ [[nodiscard]] Queue GetQueue(u32 family_index) const noexcept;
- BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
+ [[nodiscard]] BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
- ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
+ [[nodiscard]] ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
- Semaphore CreateSemaphore() const;
+ [[nodiscard]] Semaphore CreateSemaphore() const;
- Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
+ [[nodiscard]] Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
- Fence CreateFence(const VkFenceCreateInfo& ci) const;
+ [[nodiscard]] Fence CreateFence(const VkFenceCreateInfo& ci) const;
- DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
+ [[nodiscard]] DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
- RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
+ [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
- DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
+ [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
+ const VkDescriptorSetLayoutCreateInfo& ci) const;
- PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
+ [[nodiscard]] PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
- PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
+ [[nodiscard]] PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
- Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
- VkPipelineCache cache = nullptr) const;
+ [[nodiscard]] Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
+ VkPipelineCache cache = nullptr) const;
- Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
- VkPipelineCache cache = nullptr) const;
+ [[nodiscard]] Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
+ VkPipelineCache cache = nullptr) const;
- Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
+ [[nodiscard]] Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
- Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const;
+ [[nodiscard]] Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const;
- CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
+ [[nodiscard]] CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
- DescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
+ [[nodiscard]] DescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
const VkDescriptorUpdateTemplateCreateInfo& ci) const;
- QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
+ [[nodiscard]] QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
- ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
+ [[nodiscard]] ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
- Event CreateEvent() const;
+ [[nodiscard]] Event CreateEvent() const;
- SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
+ [[nodiscard]] SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
- DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
+ [[nodiscard]] DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
- DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const;
+ [[nodiscard]] DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const;
VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer,
void* pnext = nullptr) const noexcept;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 00e03bd935..c03f7a3abf 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -397,8 +397,6 @@ if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
-target_link_libraries(yuzu PRIVATE Vulkan::Headers)
-
if (UNIX AND NOT APPLE)
target_link_libraries(yuzu PRIVATE Qt6::DBus)
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 18f629f639..b825348760 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -83,8 +83,7 @@ void ConfigureDebug::SetConfiguration() {
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
#else
- ui->disable_web_applet->setEnabled(false);
- ui->disable_web_applet->setText(tr("Web applet not compiled"));
+ ui->disable_web_applet->setVisible(false);
#endif
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 4d5238643c..44ed29f141 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -95,9 +95,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include
#include
-#ifdef HAVE_SDL2
#include
#include
+
+#ifdef HAVE_SDL2
#include // For SDL ScreenSaver functions
#endif
@@ -544,6 +545,9 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
// Gen keys if necessary
OnCheckFirmwareDecryption();
+ // Check for orphaned profiles and reset profile data if necessary
+ QtCommon::Content::FixProfiles();
+
game_list->LoadCompatibilityList();
// force reload on first load to ensure add-ons get updated
game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -3946,7 +3950,7 @@ void GMainWindow::OnToggleStatusBar() {
void GMainWindow::OnGameListRefresh()
{
// Resets metadata cache and reloads
- QtCommon::Game::ResetMetadata();
+ QtCommon::Game::ResetMetadata(false);
game_list->RefreshGameDirectory();
SetFirmwareVersion();
}
diff --git a/src/yuzu/migration_worker.cpp b/src/yuzu/migration_worker.cpp
index 42ec006026..95f205ec0c 100644
--- a/src/yuzu/migration_worker.cpp
+++ b/src/yuzu/migration_worker.cpp
@@ -7,7 +7,6 @@
#include
#include
#include
-#include
#include "common/fs/path_util.h"
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000000..9abd96175b
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,21 @@
+# Tools
+
+Tools for Eden and other subprojects.
+
+## Third-Party
+
+- [CPMUtil Scripts](./cpm)
+
+## Eden
+
+- `shellcheck.sh`: Ensure POSIX compliance (and syntax sanity) for all tools in this directory and subdirectories.
+- `llvmpipe-run.sh`: Sets environment variables needed to run any command (or Eden) with llvmpipe.
+- `optimize-assets.sh`: Optimize PNG assets with OptiPng.
+- `update-cpm.sh`: Updates CPM.cmake to the latest version.
+- `update-icons.sh`: Rebuild all icons (macOS, Windows, bitmaps) based on the master SVG file (`dist/dev.eden_emu.eden.svg`)
+ * Also optimizes the master SVG
+ * Requires: `png2icns` (libicns), ImageMagick, [`svgo`](https://github.com/svg/svgo)
+- `dtrace-tool.sh`
+- `lanczos_gen.c`
+- `clang-format.sh`: Runs `clang-format` on the entire codebase.
+ * Requires: clang
diff --git a/tools/clang-format.sh b/tools/clang-format.sh
index 77c3c847ad..2deb0a3ade 100755
--- a/tools/clang-format.sh
+++ b/tools/clang-format.sh
@@ -1,3 +1,6 @@
#! /bin/sh
-exec find src -iname *.h -o -iname *.cpp | xargs clang-format-15 -i -style=file:src/.clang-format
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+exec find src -iname "*.h" -o -iname "*.cpp" | xargs clang-format -i -style=file:src/.clang-format
diff --git a/tools/cpm-fetch-all.sh b/tools/cpm-fetch-all.sh
old mode 100755
new mode 100644
index 9d5005ec44..1e7ff92a67
--- a/tools/cpm-fetch-all.sh
+++ b/tools/cpm-fetch-all.sh
@@ -1,4 +1,4 @@
-#!/bin/bash -ex
+#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -6,6 +6,12 @@
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
-LIBS=$(find . src -maxdepth 3 -name cpmfile.json -exec jq -j 'keys_unsorted | join(" ")' {} \; -printf " ")
+# provided for workflow compat
-tools/cpm-fetch.sh $LIBS
\ No newline at end of file
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+chmod +x tools/cpm/fetch.sh
+
+# shellcheck disable=SC2086
+tools/cpm/fetch.sh $LIBS
diff --git a/tools/cpm-fetch.sh b/tools/cpm-fetch.sh
deleted file mode 100755
index 088df8464e..0000000000
--- a/tools/cpm-fetch.sh
+++ /dev/null
@@ -1,236 +0,0 @@
-#!/bin/bash -e
-
-# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# SPDX-FileCopyrightText: 2025 crueter
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-[ -z "$CPM_SOURCE_CACHE" ] && CPM_SOURCE_CACHE=$PWD/.cache/cpm
-
-mkdir -p $CPM_SOURCE_CACHE
-
-ROOTDIR="$PWD"
-
-TMP=$(mktemp -d)
-
-download_package() {
- FILENAME=$(basename "$DOWNLOAD")
-
- OUTFILE="$TMP/$FILENAME"
-
- LOWER_PACKAGE=$(tr '[:upper:]' '[:lower:]' <<< "$PACKAGE_NAME")
- OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
- [ -d "$OUTDIR" ] && return
-
- curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
-
- ACTUAL_HASH=$(${HASH_ALGO}sum "$OUTFILE" | cut -d" " -f1)
- [ "$ACTUAL_HASH" != "$HASH" ] && echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
-
- mkdir -p "$OUTDIR"
-
- pushd "$OUTDIR" > /dev/null
-
- case "$FILENAME" in
- (*.7z)
- 7z x "$OUTFILE" > /dev/null
- ;;
- (*.tar*)
- tar xf "$OUTFILE" > /dev/null
- ;;
- (*.zip)
- unzip "$OUTFILE" > /dev/null
- ;;
- esac
-
- # basically if only one real item exists at the top we just move everything from there
- # since github and some vendors hate me
- DIRS=$(find -maxdepth 1 -type d -o -type f)
-
- # thanks gnu
- if [ $(wc -l <<< "$DIRS") -eq 2 ]; then
- SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
- mv "$SUBDIR"/* .
- mv "$SUBDIR"/.* . 2>/dev/null || true
- rmdir "$SUBDIR"
- fi
-
- if grep -e "patches" <<< "$JSON" > /dev/null; then
- PATCHES=$(jq -r '.patches | join(" ")' <<< "$JSON")
- for patch in $PATCHES; do
- patch --binary -p1 < "$ROOTDIR"/.patch/$package/$patch
- done
- fi
-
- popd > /dev/null
-}
-
-ci_package() {
- REPO=$(jq -r ".repo" <<< "$JSON")
- EXT=$(jq -r '.extension' <<< "$JSON")
- [ "$EXT" = null ] && EXT="tar.zst"
-
- VERSION=$(jq -r ".version" <<< "$JSON")
-
- NAME=$(jq -r ".name" <<< "$JSON")
- [ "$NAME" = null ] && NAME="$PACKAGE"
-
- PACKAGE=$(jq -r ".package | \"$package\"" <<< "$JSON")
-
- DISABLED=$(jq -j '.disabled_platforms' <<< "$JSON")
-
- [ "$REPO" = null ] && echo "No repo defined for CI package $package" && return
-
- echo "-- CI package $PACKAGE"
-
- for platform in windows-amd64 windows-arm64 android solaris freebsd linux linux-aarch64; do
- echo "-- * platform $platform"
-
- case $DISABLED in
- (*"$platform"*)
- echo "-- * -- disabled"
- continue
- ;;
- (*) ;;
- esac
-
- FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
- DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}"
- PACKAGE_NAME="$PACKAGE"
- KEY=$platform
-
- LOWER_PACKAGE=$(tr '[:upper:]' '[:lower:]' <<< "$PACKAGE_NAME")
- OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
- [ -d "$OUTDIR" ] && continue
-
- HASH_ALGO=$(jq -r ".hash_algo" <<< "$JSON")
- [ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
-
- HASH_SUFFIX="${HASH_ALGO}sum"
- HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
-
- HASH=$(curl "$HASH_URL" -sS -q -L -o -)
-
- download_package
- done
-}
-
-for package in $@
-do
- # prepare for cancer
- # TODO(crueter): Fetch json once?
- JSON=$(find . src -maxdepth 3 -name cpmfile.json -exec jq -r ".\"$package\" | select( . != null )" {} \;)
-
- [ -z "$JSON" ] && echo "!! No cpmfile definition for $package" && continue
-
- PACKAGE_NAME=$(jq -r ".package" <<< "$JSON")
- [ "$PACKAGE_NAME" = null ] && PACKAGE_NAME="$package"
-
- GIT_HOST=$(jq -r ".git_host" <<< "$JSON")
- [ "$GIT_HOST" = null ] && GIT_HOST=github.com
- REPO=$(jq -r ".repo" <<< "$JSON")
-
- CI=$(jq -r ".ci" <<< "$JSON")
- if [ "$CI" != null ]; then
- ci_package
- continue
- fi
-
- VERSION=$(jq -r ".version" <<< "$JSON")
- GIT_VERSION=$(jq -r ".git_version" <<< "$JSON")
- TAG=$(jq -r ".tag" <<< "$JSON")
- SHA=$(jq -r ".sha" <<< "$JSON")
-
- [ "$GIT_VERSION" = null ] && GIT_VERSION="$VERSION"
- [ "$GIT_VERSION" = null ] && GIT_VERSION="$TAG"
-
- # url parsing WOOOHOOHOHOOHOHOH
- URL=$(jq -r ".url" <<< "$JSON")
- SHA=$(jq -r ".sha" <<< "$JSON")
-
- VERSION=$(jq -r ".version" <<< "$JSON")
- GIT_VERSION=$(jq -r ".git_version" <<< "$JSON")
-
- if [ "$GIT_VERSION" != null ]; then
- VERSION_REPLACE="$GIT_VERSION"
- else
- VERSION_REPLACE="$VERSION"
- fi
-
- TAG=$(jq -r ".tag" <<< "$JSON")
-
- TAG=$(sed "s/%VERSION%/$VERSION_REPLACE/" <<< $TAG)
-
- ARTIFACT=$(jq -r ".artifact" <<< "$JSON")
- ARTIFACT=$(sed "s/%VERSION%/$VERSION_REPLACE/" <<< $ARTIFACT)
- ARTIFACT=$(sed "s/%TAG%/$TAG/" <<< $ARTIFACT)
-
- if [ "$URL" != "null" ]; then
- DOWNLOAD="$URL"
- elif [ "$REPO" != "null" ]; then
- GIT_URL="https://$GIT_HOST/$REPO"
-
- BRANCH=$(jq -r ".branch" <<< "$JSON")
-
- if [ "$TAG" != "null" ]; then
- if [ "$ARTIFACT" != "null" ]; then
- DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
- else
- DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
- fi
- elif [ "$SHA" != "null" ]; then
- DOWNLOAD="${GIT_URL}/archive/${SHA}.zip"
- else
- if [ "$BRANCH" = null ]; then
- BRANCH=master
- fi
-
- DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.zip"
- fi
- else
- echo "!! No repo or URL defined for $package"
- continue
- fi
-
- # key parsing
- KEY=$(jq -r ".key" <<< "$JSON")
-
- if [ "$KEY" = null ]; then
- if [ "$SHA" != null ]; then
- KEY=$(cut -c1-4 - <<< "$SHA")
- elif [ "$GIT_VERSION" != null ]; then
- KEY="$GIT_VERSION"
- elif [ "$TAG" != null ]; then
- KEY="$TAG"
- elif [ "$VERSION" != null ]; then
- KEY="$VERSION"
- else
- echo "!! No valid key could be determined for $package. Must define one of: key, sha, tag, version, git_version"
- continue
- fi
- fi
-
- echo "-- Downloading regular package $package, with key $KEY, from $DOWNLOAD"
-
- # hash parsing
- HASH_ALGO=$(jq -r ".hash_algo" <<< "$JSON")
- [ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
-
- HASH=$(jq -r ".hash" <<< "$JSON")
-
- if [ "$HASH" = null ]; then
- HASH_SUFFIX="${HASH_ALGO}sum"
- HASH_URL=$(jq -r ".hash_url" <<< "$JSON")
-
- if [ "$HASH_URL" = null ]; then
- HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
- fi
-
- HASH=$(curl "$HASH_URL" -L -o -)
- fi
-
- download_package
-done
-
-rm -rf $TMP
\ No newline at end of file
diff --git a/tools/cpm-hash.sh b/tools/cpm-hash.sh
deleted file mode 100755
index da0fb395db..0000000000
--- a/tools/cpm-hash.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-SUM=`wget -q https://github.com/$1/archive/$2.zip -O - | sha512sum`
-echo "$SUM" | cut -d " " -f1
diff --git a/tools/cpm/README.md b/tools/cpm/README.md
new file mode 100755
index 0000000000..acd7444518
--- /dev/null
+++ b/tools/cpm/README.md
@@ -0,0 +1,71 @@
+# CPMUtil Tools
+
+These are supplemental shell scripts for CPMUtil aiming to ease maintenance burden for sanity checking, updates, prefetching, formatting, and standard operations done by these shell scripts, all in one common place.
+
+All scripts are POSIX-compliant.
+
+## Meta
+
+These scripts are generally reserved for internal use.
+
+- `common.sh`: Grabs all available cpmfiles and aggregates them together.
+ * Outputs:
+ - `PACKAGES`: The aggregated cpmfile
+ - `LIBS`: The list of individual libraries contained within each cpmfile
+ - `value`: A function that grabs a key from the `JSON` variable (typically the package key)
+- `download.sh`: Utility script to handle downloading of regular and CI packages.
+ * Generally only used by the fetch scripts.
+- `package.sh`: The actual package parser.
+ * Inputs:
+ - `PACKAGE`: The package key
+ * Outputs:
+ - Basically everything. You're best off reading the code rather than me poorly explaining it.
+- `which.sh`: Find which cpmfile a package is located in.
+ * Inputs:
+ - The package key
+- `replace.sh`: Replace a package's cpmfile definition.
+ * Inputs:
+ - `PACKAGE`: The package key
+ - `NEW_JSON`: All keys to replace/add
+ * Keys not found in the new json are not touched. Keys cannot currently be deleted.
+
+## Simple Utilities
+
+These scripts don't really have any functionality, they just help you out a bit yknow?
+
+- `format.sh`: Format all cpmfiles (4-space indent is enforced)
+ * In the future, these scripts will have options for spacing
+- `hash.sh`: Determine the hash of a specific package.
+ * Inputs:
+ - The repository (e.g. fmtlib/fmt)
+ - The sha or tag (e.g. v1.0.1)
+ - `GIT_HOST`: What git host to use (default github.com)
+ - `USE_TAG`: Set to "true" if the second argument is a tag instead of a sha
+ - `ARTIFACT`: The artifact to download, if using a tag. Set to null or empty to use the tag source archive instead
+ * Output: the SHA512 sum of the package
+- `url-hash.sh`: Determine the hash of a URL
+ * Input: the URL
+ * Output: the SHA512 sum of the URL
+
+## Functional Utilities
+
+These modify the CPM cache or cpmfiles. Each allows you to input all the packages to act on, as well as a `-all.sh` that acts upon all available packages.
+
+For the update and hash scripts, set `UPDATE=true` to update the cpmfile with the new version or hash. Beware: if the hash is `cf83e1357...` that means you got a 404 error!
+
+- `fetch.sh`: Prefetch a package according to its cpmfile definition
+ * Packages are fetched to the `.cache/cpm` directory by default, following the CPMUtil default.
+ * Already-fetched packages will be skipped. You can invalidate the entire cache with `rm -rf .cache/cpm`, or invalidate a specific package with e.g. `rm -rf .cache/cpm/packagename` to force a refetch.
+ * In the future, a force option will be added
+ * Note that full prefetching will take a long time depending on your internet, the amount of dependencies, and the size of each dependency.
+- `check-updates.sh`: Check a package for available updates
+ * This only applies to packages that utilize tags.
+ * If the tag is a format string, the `git_version` is acted upon instead.
+ * Setting `FORCE=true` will forcefully update every package and its hash, even if they are on the latest version (`UPDATE` must also be true)
+ * This script generally runs fast.
+ * Packages that should skip updates (e.g. older versions or packages with poorly-made tag structures... looking at you mbedtls) may specify `"skip_updates": true` in their cpmfile definition. This is unnecessary for untagged (e.g. sha or bare URL) packages.
+- `check-hashes.sh`: Check a package's hash
+ * This only applies to packages with hardcoded hashes, NOT ones that use hash URLs.
+ * This script will take a looooooooooooooong time. This is operationally equivalent to a prefetch, and thus checking all hashes will take a while--but it's worth it! Just make sure you're not using dial-up.
+
+You are recommended to run sanity hash checking for every pull request and commit, and weekly update checks.
\ No newline at end of file
diff --git a/tools/cpm/check-hash-all.sh b/tools/cpm/check-hash-all.sh
new file mode 100755
index 0000000000..fd8c270392
--- /dev/null
+++ b/tools/cpm/check-hash-all.sh
@@ -0,0 +1,10 @@
+#!/bin/bash -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC2086
+tools/cpm/check-hash.sh $LIBS
\ No newline at end of file
diff --git a/tools/cpm/check-hash.sh b/tools/cpm/check-hash.sh
new file mode 100755
index 0000000000..85c60aad8c
--- /dev/null
+++ b/tools/cpm/check-hash.sh
@@ -0,0 +1,46 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# env vars:
+# - UPDATE: fix hashes if needed
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+RETURN=0
+
+for PACKAGE in "$@"
+do
+ export PACKAGE
+ # shellcheck disable=SC1091
+ . tools/cpm/package.sh
+
+ if [ "$CI" != null ]; then
+ continue
+ fi
+
+ [ "$HASH_URL" != null ] && continue
+ [ "$HASH_SUFFIX" != null ] && continue
+
+ echo "-- Package $PACKAGE"
+
+ [ "$HASH" = null ] && echo "-- * Warning: no hash specified" && continue
+
+ export USE_TAG=true
+ ACTUAL=$(tools/cpm/url-hash.sh "$DOWNLOAD")
+
+ # shellcheck disable=SC2028
+ [ "$ACTUAL" != "$HASH" ] && echo "-- * Expected $HASH" && echo "-- * Got $ACTUAL" && [ "$UPDATE" != "true" ] && RETURN=1
+
+ if [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; then
+ # shellcheck disable=SC2034
+ NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"")
+ export NEW_JSON
+
+ tools/cpm/replace.sh
+ fi
+done
+
+exit $RETURN
\ No newline at end of file
diff --git a/tools/cpm/check-updates-all.sh b/tools/cpm/check-updates-all.sh
new file mode 100755
index 0000000000..a6eda58bac
--- /dev/null
+++ b/tools/cpm/check-updates-all.sh
@@ -0,0 +1,10 @@
+#!/bin/bash -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC2086
+tools/cpm/check-updates.sh $LIBS
\ No newline at end of file
diff --git a/tools/cpm/check-updates.sh b/tools/cpm/check-updates.sh
new file mode 100755
index 0000000000..bdccf96ca2
--- /dev/null
+++ b/tools/cpm/check-updates.sh
@@ -0,0 +1,90 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# env vars:
+# - UPDATE: update if available
+# - FORCE: forcefully update
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+RETURN=0
+
+filter() {
+ TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\") | not)]") # vulkan
+}
+
+for PACKAGE in "$@"
+do
+ export PACKAGE
+ # shellcheck disable=SC1091
+ . tools/cpm/package.sh
+
+ SKIP=$(value "skip_updates")
+
+ [ "$SKIP" = "true" ] && continue
+
+ [ "$REPO" = null ] && continue
+ [ "$GIT_HOST" != "github.com" ] && continue # TODO
+ # shellcheck disable=SC2153
+ [ "$TAG" = null ] && continue
+
+ echo "-- Package $PACKAGE"
+
+ # TODO(crueter): Support for Forgejo updates w/ forgejo_token
+ # Use gh-cli to avoid ratelimits lmao
+ TAGS=$(gh api --method GET "/repos/$REPO/tags")
+
+ # filter out some commonly known annoyances
+ # TODO add more
+
+ filter vulkan-sdk # vulkan
+ filter yotta # mbedtls
+
+ # ignore betas/alphas (remove if needed)
+ filter alpha
+ filter beta
+ filter rc
+
+ # Add package-specific overrides here, e.g. here for fmt:
+ [ "$PACKAGE" = fmt ] && filter v0.11
+
+ LATEST=$(echo "$TAGS" | jq -r '.[0].name')
+
+ [ "$LATEST" = "$TAG" ] && [ "$FORCE" != "true" ] && echo "-- * Up-to-date" && continue
+
+ RETURN=1
+
+ if [ "$HAS_REPLACE" = "true" ]; then
+ # this just extracts the tag prefix
+ VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1)
+
+ # then we strip out the prefix from the new tag, and make that our new git_version
+ NEW_GIT_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g")
+ fi
+
+ echo "-- * Version $LATEST available, current is $TAG"
+
+ export USE_TAG=true
+ HASH=$(tools/cpm/hash.sh "$REPO" "$LATEST")
+
+ echo "-- * New hash: $HASH"
+
+ if [ "$UPDATE" = "true" ]; then
+ RETURN=0
+
+ if [ "$HAS_REPLACE" = "true" ]; then
+ NEW_JSON=$(echo "$JSON" | jq ".hash = \"$HASH\" | .git_version = \"$NEW_GIT_VERSION\"")
+ else
+ NEW_JSON=$(echo "$JSON" | jq ".hash = \"$HASH\" | .tag = \"$LATEST\"")
+ fi
+
+ export NEW_JSON
+
+ tools/cpm/replace.sh
+ fi
+done
+
+exit $RETURN
\ No newline at end of file
diff --git a/tools/cpm/common.sh b/tools/cpm/common.sh
new file mode 100755
index 0000000000..4aff058bdc
--- /dev/null
+++ b/tools/cpm/common.sh
@@ -0,0 +1,32 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+##################################
+# CHANGE THESE FOR YOUR PROJECT! #
+##################################
+
+# Which directories to search
+DIRS=". src"
+
+# How many levels to go (3 is 2 subdirs max)
+MAXDEPTH=3
+
+# shellcheck disable=SC2038
+# shellcheck disable=SC2016
+# shellcheck disable=SC2086
+[ -z "$PACKAGES" ] && PACKAGES=$(find $DIRS -maxdepth "$MAXDEPTH" -name cpmfile.json | xargs jq -s 'reduce .[] as $item ({}; . * $item)')
+
+# For your project you'll want to change the PACKAGES call to include whatever locations you may use (externals, src, etc.)
+# Always include .
+LIBS=$(echo "$PACKAGES" | jq -j 'keys_unsorted | join(" ")')
+
+export PACKAGES
+export LIBS
+export DIRS
+export MAXDEPTH
+
+value() {
+ echo "$JSON" | jq -r ".$1"
+}
\ No newline at end of file
diff --git a/tools/cpm/download.sh b/tools/cpm/download.sh
new file mode 100755
index 0000000000..426f1f51e6
--- /dev/null
+++ b/tools/cpm/download.sh
@@ -0,0 +1,100 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# env vars:
+# - UPDATE: fix hashes if needed
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+download_package() {
+ FILENAME=$(basename "$DOWNLOAD")
+
+ OUTFILE="$TMP/$FILENAME"
+
+ LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
+ OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
+ [ -d "$OUTDIR" ] && return
+
+ curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
+
+ ACTUAL_HASH=$("${HASH_ALGO}"sum "$OUTFILE" | cut -d" " -f1)
+ [ "$ACTUAL_HASH" != "$HASH" ] && echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
+
+ mkdir -p "$OUTDIR"
+
+ PREVDIR="$PWD"
+ cd "$OUTDIR"
+
+ case "$FILENAME" in
+ (*.7z)
+ 7z x "$OUTFILE" > /dev/null
+ ;;
+ (*.tar*)
+ tar xf "$OUTFILE" > /dev/null
+ ;;
+ (*.zip)
+ unzip "$OUTFILE" > /dev/null
+ ;;
+ esac
+
+ # basically if only one real item exists at the top we just move everything from there
+ # since github and some vendors hate me
+ DIRS=$(find . -maxdepth 1 -type d -o -type f)
+
+ # thanks gnu
+ if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
+ SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
+ mv "$SUBDIR"/* .
+ mv "$SUBDIR"/.* . 2>/dev/null || true
+ rmdir "$SUBDIR"
+ fi
+
+ if echo "$JSON" | grep -e "patches" > /dev/null; then
+ PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")')
+ for patch in $PATCHES; do
+ # shellcheck disable=SC2154
+ patch --binary -p1 < "$ROOTDIR/.patch/$PACKAGE/$patch"
+ done
+ fi
+
+ cd "$PREVDIR"
+}
+
+ci_package() {
+ [ "$REPO" = null ] && echo "-- ! No repo defined" && return
+
+ echo "-- CI package $PACKAGE_NAME"
+
+ for platform in windows-amd64 windows-arm64 android solaris-amd64 freebsd-amd64 linux-amd64 linux-aarch64 macos-universal; do
+ echo "-- * platform $platform"
+
+ case $DISABLED in
+ (*"$platform"*)
+ echo "-- * -- disabled"
+ continue
+ ;;
+ (*) ;;
+ esac
+
+ FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
+ DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}"
+ KEY=$platform
+
+ LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
+ OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
+ [ -d "$OUTDIR" ] && continue
+
+ HASH_ALGO=$(value "hash_algo")
+ [ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
+
+ HASH_SUFFIX="${HASH_ALGO}sum"
+ HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
+
+ HASH=$(curl "$HASH_URL" -sS -q -L -o -)
+
+ download_package
+ done
+}
diff --git a/tools/cpm/fetch-all.sh b/tools/cpm/fetch-all.sh
new file mode 100755
index 0000000000..5c41b5d080
--- /dev/null
+++ b/tools/cpm/fetch-all.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC2086
+tools/cpm/fetch.sh $LIBS
\ No newline at end of file
diff --git a/tools/cpm/fetch.sh b/tools/cpm/fetch.sh
new file mode 100755
index 0000000000..bf45676cfa
--- /dev/null
+++ b/tools/cpm/fetch.sh
@@ -0,0 +1,36 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[ -z "$CPM_SOURCE_CACHE" ] && CPM_SOURCE_CACHE=$PWD/.cache/cpm
+
+mkdir -p "$CPM_SOURCE_CACHE"
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC1091
+. tools/cpm/download.sh
+
+# shellcheck disable=SC2034
+ROOTDIR="$PWD"
+
+TMP=$(mktemp -d)
+
+# shellcheck disable=SC2034
+for PACKAGE in "$@"
+do
+ export PACKAGE
+ # shellcheck disable=SC1091
+ . tools/cpm/package.sh
+
+ if [ "$CI" = "true" ]; then
+ ci_package
+ else
+ echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD"
+ download_package
+ fi
+done
+
+rm -rf "$TMP"
\ No newline at end of file
diff --git a/tools/cpm/format.sh b/tools/cpm/format.sh
new file mode 100755
index 0000000000..8d99b4796b
--- /dev/null
+++ b/tools/cpm/format.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC2086
+FILES=$(find $DIRS -maxdepth "$MAXDEPTH" -name cpmfile.json)
+
+for file in $FILES; do
+ jq --indent 4 < "$file" > "$file".new
+ mv "$file".new "$file"
+done
diff --git a/tools/cpm/hash.sh b/tools/cpm/hash.sh
new file mode 100755
index 0000000000..27061bd9a4
--- /dev/null
+++ b/tools/cpm/hash.sh
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# usage: hash.sh repo tag-or-sha
+# env vars: GIT_HOST, USE_TAG (use tag instead of sha), ARTIFACT (download artifact with that name instead of src archive)
+
+REPO="$1"
+[ -z "$GIT_HOST" ] && GIT_HOST=github.com
+GIT_URL="https://$GIT_HOST/$REPO"
+
+if [ "$USE_TAG" = "true" ]; then
+ if [ -z "$ARTIFACT" ] || [ "$ARTIFACT" = "null" ]; then
+ URL="${GIT_URL}/archive/refs/tags/$2.tar.gz"
+ else
+ URL="${GIT_URL}/releases/download/$2/${ARTIFACT}"
+ fi
+else
+ URL="${GIT_URL}/archive/$2.zip"
+fi
+
+SUM=$(wget -q "$URL" -O - | sha512sum)
+
+echo "$SUM" | cut -d " " -f1
diff --git a/tools/cpm/package.sh b/tools/cpm/package.sh
new file mode 100755
index 0000000000..d82b2fcbe9
--- /dev/null
+++ b/tools/cpm/package.sh
@@ -0,0 +1,203 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# env vars:
+# - UPDATE: fix hashes if needed
+
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+[ -z "$PACKAGE" ] && echo "Package was not specified" && exit 0
+
+# shellcheck disable=SC2153
+JSON=$(echo "$PACKAGES" | jq -r ".\"$PACKAGE\" | select( . != null )")
+
+[ -z "$JSON" ] && echo "!! No cpmfile definition for $PACKAGE" && exit 1
+
+# unset stuff
+export PACKAGE_NAME="null"
+export REPO="null"
+export CI="null"
+export GIT_HOST="null"
+export EXT="null"
+export NAME="null"
+export DISABLED="null"
+export TAG="null"
+export ARTIFACT="null"
+export SHA="null"
+export VERSION="null"
+export GIT_VERSION="null"
+export DOWNLOAD="null"
+export URL="null"
+export KEY="null"
+export HASH="null"
+export ORIGINAL_TAG="null"
+export HAS_REPLACE="null"
+export VERSION_REPLACE="null"
+export HASH_URL="null"
+export HASH_SUFFIX="null"
+export HASH_ALGO="null"
+
+########
+# Meta #
+########
+
+REPO=$(value "repo")
+CI=$(value "ci")
+
+PACKAGE_NAME=$(value "package")
+[ "$PACKAGE_NAME" = null ] && PACKAGE_NAME="$PACKAGE"
+
+GIT_HOST=$(value "git_host")
+[ "$GIT_HOST" = null ] && GIT_HOST=github.com
+
+export PACKAGE_NAME
+export REPO
+export CI
+export GIT_HOST
+
+######################
+# CI Package Parsing #
+######################
+
+VERSION=$(value "version")
+
+if [ "$CI" = "true" ]; then
+ EXT=$(value "extension")
+ [ "$EXT" = null ] && EXT="tar.zst"
+
+ NAME=$(value "name")
+ DISABLED=$(echo "$JSON" | jq -j '.disabled_platforms')
+
+ [ "$NAME" = null ] && NAME="$PACKAGE_NAME"
+
+ export EXT
+ export NAME
+ export DISABLED
+ export VERSION
+
+ return 0
+fi
+
+##############
+# Versioning #
+##############
+
+TAG=$(value "tag")
+ARTIFACT=$(value "artifact")
+SHA=$(value "sha")
+GIT_VERSION=$(value "git_version")
+
+[ "$GIT_VERSION" = null ] && GIT_VERSION="$VERSION"
+
+if [ "$GIT_VERSION" != null ]; then
+ VERSION_REPLACE="$GIT_VERSION"
+else
+ VERSION_REPLACE="$VERSION"
+fi
+
+echo "$TAG" | grep -e "%VERSION%" > /dev/null && HAS_REPLACE=true || HAS_REPLACE=false
+ORIGINAL_TAG="$TAG"
+
+TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION_REPLACE/g")
+ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION_REPLACE/g")
+ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g")
+
+export TAG
+export ARTIFACT
+export SHA
+export VERSION
+export GIT_VERSION
+export ORIGINAL_TAG
+export HAS_REPLACE
+export VERSION_REPLACE
+
+###############
+# URL Parsing #
+###############
+
+URL=$(value "url")
+
+if [ "$URL" != "null" ]; then
+ DOWNLOAD="$URL"
+elif [ "$REPO" != "null" ]; then
+ GIT_URL="https://$GIT_HOST/$REPO"
+
+ BRANCH=$(value "branch")
+
+ if [ "$TAG" != "null" ]; then
+ if [ "$ARTIFACT" != "null" ]; then
+ DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
+ else
+ DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
+ fi
+ elif [ "$SHA" != "null" ]; then
+ DOWNLOAD="${GIT_URL}/archive/${SHA}.zip"
+ else
+ if [ "$BRANCH" = null ]; then
+ BRANCH=master
+ fi
+
+ DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.zip"
+ fi
+else
+ echo "!! No repo or URL defined for $PACKAGE"
+ exit 1
+fi
+
+export DOWNLOAD
+export URL
+
+###############
+# Key Parsing #
+###############
+
+KEY=$(value "key")
+
+if [ "$KEY" = null ]; then
+ if [ "$SHA" != null ]; then
+ KEY=$(echo "$SHA" | cut -c1-4)
+ elif [ "$GIT_VERSION" != null ]; then
+ KEY="$GIT_VERSION"
+ elif [ "$TAG" != null ]; then
+ KEY="$TAG"
+ elif [ "$VERSION" != null ]; then
+ KEY="$VERSION"
+ else
+ echo "!! No valid key could be determined for $PACKAGE. Must define one of: key, sha, tag, version, git_version"
+ exit 1
+ fi
+fi
+
+export KEY
+
+################
+# Hash Parsing #
+################
+
+HASH_ALGO=$(value "hash_algo")
+[ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
+
+HASH=$(value "hash")
+
+if [ "$HASH" = null ]; then
+ HASH_SUFFIX="${HASH_ALGO}sum"
+ HASH_URL=$(value "hash_url")
+
+ if [ "$HASH_URL" = null ]; then
+ HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
+ fi
+
+ HASH=$(curl "$HASH_URL" -Ss -L -o -)
+else
+ HASH_URL=null
+ HASH_SUFFIX=null
+fi
+
+export HASH_URL
+export HASH_SUFFIX
+export HASH
+export HASH_ALGO
+export JSON
\ No newline at end of file
diff --git a/tools/cpm/replace.sh b/tools/cpm/replace.sh
new file mode 100755
index 0000000000..501cfda6b1
--- /dev/null
+++ b/tools/cpm/replace.sh
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Replace a specified package with a modified json.
+
+# env vars:
+# - PACKAGE: The package key to act on
+# - NEW_JSON: The new json to use
+
+[ -z "$PACKAGE" ] && echo "You must provide the PACKAGE environment variable." && return 1
+[ -z "$NEW_JSON" ] && echo "You must provide the NEW_JSON environment variable." && return 1
+
+FILE=$(tools/cpm/which.sh "$PACKAGE")
+
+jq --indent 4 --argjson repl "$NEW_JSON" ".\"$PACKAGE\" *= \$repl" "$FILE" > "$FILE".new
+mv "$FILE".new "$FILE"
+
+echo "-- * -- Updated $FILE"
diff --git a/tools/cpm/url-hash.sh b/tools/cpm/url-hash.sh
new file mode 100755
index 0000000000..c911dacb37
--- /dev/null
+++ b/tools/cpm/url-hash.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+SUM=$(wget -q "$1" -O - | sha512sum)
+echo "$SUM" | cut -d " " -f1
diff --git a/tools/cpm/which.sh b/tools/cpm/which.sh
new file mode 100755
index 0000000000..c936d0a97f
--- /dev/null
+++ b/tools/cpm/which.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: 2025 crueter
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# check which file a package is in
+# shellcheck disable=SC1091
+. tools/cpm/common.sh
+
+# shellcheck disable=SC2086
+JSON=$(find $DIRS -maxdepth "$MAXDEPTH" -name cpmfile.json -exec grep -l "$1" {} \;)
+
+[ -z "$JSON" ] && echo "!! No cpmfile definition for $1"
+
+echo "$JSON"
\ No newline at end of file
diff --git a/tools/dtrace-tool.sh b/tools/dtrace-tool.sh
index a8cc4c7bad..4a75848fcd 100755
--- a/tools/dtrace-tool.sh
+++ b/tools/dtrace-tool.sh
@@ -1,42 +1,59 @@
#!/usr/local/bin/bash -ex
+
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
+
# Basic script to run dtrace sampling over the program (requires Flamegraph)
# Usage is either running as: ./dtrace-tool.sh pid (then input the pid of the process)
# Or just run directly with: ./dtrace-tool.sh
+
FLAMEGRAPH_DIR=".."
-function fail {
- printf '%s\n' "$1" >&2
- exit "${2-1}"
+fail() {
+ printf '%s\n' "$1" >&2
+ exit "${2-1}"
}
+
[ -f $FLAMEGRAPH_DIR/FlameGraph/stackcollapse.pl ] || fail 'Where is flamegraph?'
#[ which dtrace ] || fail 'Needs DTrace installed'
-read -p "Sampling Hz [800]: " TRACE_CFG_HZ
+
+read -r "Sampling Hz [800]: " TRACE_CFG_HZ
if [ -z "${TRACE_CFG_HZ}" ]; then
- TRACE_CFG_HZ=800
+ TRACE_CFG_HZ=800
fi
-read -p "Sampling time [5] sec: " TRACE_CFG_TIME
+
+read -r "Sampling time [5] sec: " TRACE_CFG_TIME
if [ -z "${TRACE_CFG_TIME}" ]; then
- TRACE_CFG_TIME=5
+ TRACE_CFG_TIME=5
fi
+
TRACE_FILE=dtrace-out.user_stacks
TRACE_FOLD=dtrace-out.fold
TRACE_SVG=dtrace-out.svg
ps
-if [[ $1 = 'pid' ]]; then
- read -p "PID: " TRACE_CFG_PID
- sudo echo 'Sudo!'
+
+if [ "$1" = 'pid' ]; then
+ read -r "PID: " TRACE_CFG_PID
+ sudo echo 'Sudo!'
else
- [[ -f $1 && $1 ]] || fail 'Usage: ./tools/dtrace-profile.sh '
- echo "Executing: '$@'"
- sudo echo 'Sudo!'
- "$@" &
- TRACE_CFG_PID=$!
+ if [ -f "$1" ] && [ "$1" ]; then
+ fail 'Usage: ./tools/dtrace-profile.sh '
+ fi
+
+ printf "Executing: "
+ echo "$@"
+ sudo echo 'Sudo!'
+ "$@" &
+ TRACE_CFG_PID=$!
fi
+
TRACE_PROBE="profile-${TRACE_CFG_HZ} /pid == ${TRACE_CFG_PID} && arg1/ { @[ustack()] = count(); } tick-${TRACE_CFG_TIME}s { exit(0); }"
+
rm -- $TRACE_SVG || echo 'Skip'
+
sudo dtrace -x ustackframes=100 -Z -n "$TRACE_PROBE" -o $TRACE_FILE 2>/dev/null || exit
+
perl $FLAMEGRAPH_DIR/FlameGraph/stackcollapse.pl $TRACE_FILE > $TRACE_FOLD || exit
perl $FLAMEGRAPH_DIR/FlameGraph/flamegraph.pl $TRACE_FOLD > $TRACE_SVG || exit
+
sudo chmod 0666 $TRACE_FILE
rm -- $TRACE_FILE $TRACE_FOLD
\ No newline at end of file
diff --git a/tools/llvmpipe-run.sh b/tools/llvmpipe-run.sh
index 8f53d691c2..c3a5a85d41 100755
--- a/tools/llvmpipe-run.sh
+++ b/tools/llvmpipe-run.sh
@@ -1,15 +1,205 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
+
# This script basically allows you to "dirtily" use llvmpipe in any configuration
# llvmpipe will of course, run at negative mach-1 speeds. However this is mainly useful to test that mesa
# is properly working on extraneous systems (such as Managarm). This mainly only works with MESA >22.0.0
# Use as follows: ./llvmpipe-run.sh
+
export MESA_GL_VERSION_OVERRIDE=4.6
export MESA_GLSL_VERSION_OVERRIDE=460
export MESA_EXTENSION_MAX_YEAR=2025
export MESA_DEBUG=1
export MESA_VK_VERSION_OVERRIDE=1.3
export LIBGL_ALWAYS_SOFTWARE=1
-export MESA_EXTENSION_OVERRIDE="GL_AMD_multi_draw_indirect GL_AMD_seamless_cubemap_per_texture GL_AMD_vertex_shader_layer GL_AMD_vertex_shader_viewport_index GL_ARB_ES2_compatibility GL_ARB_ES3_1_compatibility GL_ARB_ES3_2_compatibility GL_ARB_ES3_compatibility GL_ARB_arrays_of_arrays GL_ARB_base_instance GL_ARB_bindless_texture GL_ARB_blend_func_extended GL_ARB_buffer_storage GL_ARB_clear_buffer_object GL_ARB_clear_texture GL_ARB_clip_control GL_ARB_color_buffer_float GL_ARB_compressed_texture_pixel_storage GL_ARB_compute_shader GL_ARB_compute_variable_group_size GL_ARB_conditional_render_inverted GL_ARB_conservative_depth GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance GL_ARB_debug_output GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers GL_ARB_draw_buffers_blend GL_ARB_draw_elements_base_vertex GL_ARB_draw_indirect GL_ARB_draw_instanced GL_ARB_enhanced_layouts GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions GL_ARB_fragment_layer_viewport GL_ARB_fragment_program GL_ARB_fragment_program_shadow GL_ARB_fragment_shader GL_ARB_fragment_shader_interlock GL_ARB_framebuffer_no_attachments GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary GL_ARB_get_texture_sub_image GL_ARB_gl_spirv GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64 GL_ARB_gpu_shader_int64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_imaging GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2 GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture GL_ARB_occlusion_query GL_ARB_occlusion_query2 GL_ARB_parallel_shader_compile GL_ARB_pipeline_statistics_query GL_ARB_pixel_buffer_object GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_polygon_offset_clamp GL_ARB_post_depth_coverage GL_ARB_program_interface_query GL_ARB_provoking_vertex GL_ARB_query_buffer_object GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_sample_locations GL_ARB_sample_shading GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_seamless_cubemap_per_texture GL_ARB_separate_shader_objects GL_ARB_shader_atomic_counter_ops GL_ARB_shader_atomic_counters GL_ARB_shader_ballot GL_ARB_shader_bit_encoding GL_ARB_shader_clock GL_ARB_shader_draw_parameters GL_ARB_shader_group_vote GL_ARB_shader_image_load_store GL_ARB_shader_image_size GL_ARB_shader_objects GL_ARB_shader_precision GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine GL_ARB_shader_texture_image_samples GL_ARB_shader_texture_lod GL_ARB_shader_viewport_layer_array GL_ARB_shading_language_100 GL_ARB_shading_language_420pack GL_ARB_shading_language_include GL_ARB_shading_language_packing GL_ARB_shadow GL_ARB_sparse_buffer GL_ARB_sparse_texture GL_ARB_sparse_texture2 GL_ARB_sparse_texture_clamp GL_ARB_spirv_extensions GL_ARB_stencil_texturing GL_ARB_sync GL_ARB_tessellation_shader GL_ARB_texture_barrier GL_ARB_texture_border_clamp GL_ARB_texture_buffer_object GL_ARB_texture_buffer_object_rgb32 GL_ARB_texture_buffer_range GL_ARB_texture_compression GL_ARB_texture_compression_bptc GL_ARB_texture_compression_rgtc GL_ARB_texture_cube_map GL_ARB_texture_cube_map_array GL_ARB_texture_env_add GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_filter_anisotropic GL_ARB_texture_filter_minmax GL_ARB_texture_float GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample GL_ARB_texture_non_power_of_two GL_ARB_texture_query_levels GL_ARB_texture_query_lod GL_ARB_texture_rectangle GL_ARB_texture_rg GL_ARB_texture_rgb10_a2ui GL_ARB_texture_stencil8 GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_swizzle GL_ARB_texture_view GL_ARB_timer_query GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 GL_ARB_transform_feedback_instanced GL_ARB_transform_feedback_overflow_query GL_ARB_transpose_matrix GL_ARB_uniform_buffer_object GL_ARB_vertex_array_bgra GL_ARB_vertex_array_object GL_ARB_vertex_attrib_64bit GL_ARB_vertex_attrib_binding GL_ARB_vertex_buffer_object GL_ARB_vertex_program GL_ARB_vertex_shader GL_ARB_vertex_type_10f_11f_11f_rev GL_ARB_vertex_type_2_10_10_10_rev GL_ARB_viewport_array GL_ARB_window_pos GL_ATI_draw_buffers GL_ATI_texture_float GL_ATI_texture_mirror_once GL_EXTX_framebuffer_mixed_formats GL_EXT_Cg_shader GL_EXT_abgr GL_EXT_bgra GL_EXT_bindable_uniform GL_EXT_blend_color GL_EXT_blend_equation_separate GL_EXT_blend_func_separate GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_compiled_vertex_array GL_EXT_depth_bounds_test GL_EXT_direct_state_access GL_EXT_draw_buffers2 GL_EXT_draw_instanced GL_EXT_draw_range_elements GL_EXT_fog_coord GL_EXT_framebuffer_blit GL_EXT_framebuffer_multisample GL_EXT_framebuffer_multisample_blit_scaled GL_EXT_framebuffer_object GL_EXT_framebuffer_sRGB GL_EXT_geometry_shader4 GL_EXT_gpu_program_parameters GL_EXT_gpu_shader4 GL_EXT_import_sync_object GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_multi_draw_arrays GL_EXT_multiview_texture_multisample GL_EXT_multiview_timer_query GL_EXT_packed_depth_stencil GL_EXT_packed_float GL_EXT_packed_pixels GL_EXT_pixel_buffer_object GL_EXT_point_parameters GL_EXT_polygon_offset_clamp GL_EXT_post_depth_coverage GL_EXT_provoking_vertex GL_EXT_raster_multisample GL_EXT_rescale_normal GL_EXT_secondary_color GL_EXT_semaphore GL_EXT_semaphore_fd GL_EXT_separate_shader_objects GL_EXT_separate_specular_color GL_EXT_shader_image_load_formatted GL_EXT_shader_image_load_store GL_EXT_shader_integer_mix GL_EXT_shadow_funcs GL_EXT_sparse_texture2 GL_EXT_stencil_two_side GL_EXT_stencil_wrap GL_EXT_texture3D GL_EXT_texture_array GL_EXT_texture_buffer_object GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_latc GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map GL_EXT_texture_edge_clamp GL_EXT_texture_env_add GL_EXT_texture_env_combine GL_EXT_texture_env_dot3 GL_EXT_texture_filter_anisotropic GL_EXT_texture_filter_minmax GL_EXT_texture_integer GL_EXT_texture_lod GL_EXT_texture_lod_bias GL_EXT_texture_mirror_clamp GL_EXT_texture_object GL_EXT_texture_sRGB GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_shared_exponent GL_EXT_texture_storage GL_EXT_texture_swizzle GL_EXT_timer_query GL_EXT_transform_feedback2 GL_EXT_vertex_array GL_EXT_vertex_array_bgra GL_EXT_vertex_attrib_64bit GL_EXT_window_rectangles GL_EXT_x11_sync_object GL_IBM_rasterpos_clip GL_IBM_texture_mirrored_repeat GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent GL_KHR_context_flush_control GL_KHR_debug GL_KHR_no_error GL_KHR_parallel_shader_compile GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_shader_subgroup GL_KTX_buffer_region GL_NVX_blend_equation_advanced_multi_draw_buffers GL_NVX_conditional_render GL_NVX_gpu_memory_info GL_NVX_nvenc_interop GL_NVX_progress_fence GL_NV_ES1_1_compatibility GL_NV_ES3_1_compatibility GL_NV_alpha_to_coverage_dither_control GL_NV_bindless_multi_draw_indirect GL_NV_bindless_multi_draw_indirect_count GL_NV_bindless_texture GL_NV_blend_equation_advanced GL_NV_blend_equation_advanced_coherent GL_NV_blend_minmax_factor GL_NV_blend_square GL_NV_clip_space_w_scaling GL_NV_command_list GL_NV_compute_program5 GL_NV_conditional_render GL_NV_conservative_raster GL_NV_conservative_raster_dilate GL_NV_conservative_raster_pre_snap_triangles GL_NV_copy_depth_to_color GL_NV_copy_image GL_NV_depth_buffer_float GL_NV_depth_clamp GL_NV_draw_texture GL_NV_draw_vulkan_image GL_NV_explicit_multisample GL_NV_feature_query GL_NV_fence GL_NV_fill_rectangle GL_NV_float_buffer GL_NV_fog_distance GL_NV_fragment_coverage_to_color GL_NV_fragment_program GL_NV_fragment_program2 GL_NV_fragment_program_option GL_NV_fragment_shader_interlock GL_NV_framebuffer_mixed_samples GL_NV_framebuffer_multisample_coverage GL_NV_geometry_shader4 GL_NV_geometry_shader_passthrough GL_NV_gpu_multicast GL_NV_gpu_program4 GL_NV_gpu_program4_1 GL_NV_gpu_program5 GL_NV_gpu_program5_mem_extended GL_NV_gpu_program_fp64 GL_NV_gpu_program_multiview GL_NV_gpu_shader5 GL_NV_half_float GL_NV_internalformat_sample_query GL_NV_light_max_exponent GL_NV_memory_attachment GL_NV_memory_object_sparse GL_NV_multisample_coverage GL_NV_multisample_filter_hint GL_NV_occlusion_query GL_NV_packed_depth_stencil GL_NV_parameter_buffer_object GL_NV_parameter_buffer_object2 GL_NV_path_rendering GL_NV_path_rendering_shared_edge GL_NV_pixel_data_range GL_NV_point_sprite GL_NV_primitive_restart GL_NV_query_resource GL_NV_query_resource_tag GL_NV_register_combiners GL_NV_register_combiners2 GL_NV_robustness_video_memory_purge GL_NV_sample_locations GL_NV_sample_mask_override_coverage GL_NV_shader_atomic_counters GL_NV_shader_atomic_float GL_NV_shader_atomic_float64 GL_NV_shader_atomic_fp16_vector GL_NV_shader_atomic_int64 GL_NV_shader_buffer_load GL_NV_shader_storage_buffer_object GL_NV_shader_subgroup_partitioned GL_NV_shader_thread_group GL_NV_shader_thread_shuffle GL_NV_stereo_view_rendering GL_NV_texgen_reflection GL_NV_texture_barrier GL_NV_texture_compression_vtc GL_NV_texture_env_combine4 GL_NV_texture_multisample GL_NV_texture_rectangle GL_NV_texture_rectangle_compressed GL_NV_texture_shader GL_NV_texture_shader2 GL_NV_texture_shader3 GL_NV_timeline_semaphore GL_NV_transform_feedback GL_NV_transform_feedback2 GL_NV_uniform_buffer_std430_layout GL_NV_uniform_buffer_unified_memory GL_NV_vdpau_interop GL_NV_vdpau_interop2 GL_NV_vertex_array_range GL_NV_vertex_array_range2 GL_NV_vertex_attrib_integer_64bit GL_NV_vertex_buffer_unified_memory GL_NV_vertex_program GL_NV_vertex_program1_1 GL_NV_vertex_program2 GL_NV_vertex_program2_option GL_NV_vertex_program3 GL_NV_viewport_array2 GL_NV_viewport_swizzle GL_OVR_multiview GL_OVR_multiview2 GL_S3_s3tc GL_SGIS_generate_mipmap GL_SGIS_texture_lod GL_SGIX_depth_texture GL_SGIX_shadow GL_SUN_slice_accum GL_AMD_multi_draw_indirect GL_AMD_seamless_cubemap_per_texture GL_AMD_vertex_shader_layer GL_AMD_vertex_shader_viewport_index GL_ARB_ES2_compatibility GL_ARB_ES3_1_compatibility GL_ARB_ES3_2_compatibility GL_ARB_ES3_compatibility GL_ARB_arrays_of_arrays GL_ARB_base_instance GL_ARB_bindless_texture GL_ARB_blend_func_extended GL_ARB_buffer_storage GL_ARB_clear_buffer_object GL_ARB_clear_texture GL_ARB_clip_control GL_ARB_color_buffer_float GL_ARB_compatibility GL_ARB_compressed_texture_pixel_storage GL_ARB_compute_shader GL_ARB_compute_variable_group_size GL_ARB_conditional_render_inverted GL_ARB_conservative_depth GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance GL_ARB_debug_output GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers GL_ARB_draw_buffers_blend GL_ARB_draw_elements_base_vertex GL_ARB_draw_indirect GL_ARB_draw_instanced GL_ARB_enhanced_layouts GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions GL_ARB_fragment_layer_viewport GL_ARB_fragment_program GL_ARB_fragment_program_shadow GL_ARB_fragment_shader GL_ARB_fragment_shader_interlock GL_ARB_framebuffer_no_attachments GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary GL_ARB_get_texture_sub_image GL_ARB_gl_spirv GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64 GL_ARB_gpu_shader_int64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_imaging GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2 GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture GL_ARB_occlusion_query GL_ARB_occlusion_query2 GL_ARB_parallel_shader_compile GL_ARB_pipeline_statistics_query GL_ARB_pixel_buffer_object GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_polygon_offset_clamp GL_ARB_post_depth_coverage GL_ARB_program_interface_query GL_ARB_provoking_vertex GL_ARB_query_buffer_object GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_sample_locations GL_ARB_sample_shading GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_seamless_cubemap_per_texture GL_ARB_separate_shader_objects GL_ARB_shader_atomic_counter_ops GL_ARB_shader_atomic_counters GL_ARB_shader_ballot GL_ARB_shader_bit_encoding GL_ARB_shader_clock GL_ARB_shader_draw_parameters GL_ARB_shader_group_vote GL_ARB_shader_image_load_store GL_ARB_shader_image_size GL_ARB_shader_objects GL_ARB_shader_precision GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine GL_ARB_shader_texture_image_samples GL_ARB_shader_texture_lod GL_ARB_shader_viewport_layer_array GL_ARB_shading_language_100 GL_ARB_shading_language_420pack GL_ARB_shading_language_include GL_ARB_shading_language_packing GL_ARB_shadow GL_ARB_sparse_buffer GL_ARB_sparse_texture GL_ARB_sparse_texture2 GL_ARB_sparse_texture_clamp GL_ARB_spirv_extensions GL_ARB_stencil_texturing GL_ARB_sync GL_ARB_tessellation_shader GL_ARB_texture_barrier GL_ARB_texture_border_clamp GL_ARB_texture_buffer_object GL_ARB_texture_buffer_object_rgb32 GL_ARB_texture_buffer_range GL_ARB_texture_compression GL_ARB_texture_compression_bptc GL_ARB_texture_compression_rgtc GL_ARB_texture_cube_map GL_ARB_texture_cube_map_array GL_ARB_texture_env_add GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_filter_anisotropic GL_ARB_texture_filter_minmax GL_ARB_texture_float GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample GL_ARB_texture_non_power_of_two GL_ARB_texture_query_levels GL_ARB_texture_query_lod GL_ARB_texture_rectangle GL_ARB_texture_rg GL_ARB_texture_rgb10_a2ui GL_ARB_texture_stencil8 GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_swizzle GL_ARB_texture_view GL_ARB_timer_query GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 GL_ARB_transform_feedback_instanced GL_ARB_transform_feedback_overflow_query GL_ARB_transpose_matrix GL_ARB_uniform_buffer_object GL_ARB_vertex_array_bgra GL_ARB_vertex_array_object GL_ARB_vertex_attrib_64bit GL_ARB_vertex_attrib_binding GL_ARB_vertex_buffer_object GL_ARB_vertex_program GL_ARB_vertex_shader GL_ARB_vertex_type_10f_11f_11f_rev GL_ARB_vertex_type_2_10_10_10_rev GL_ARB_viewport_array GL_ARB_window_pos GL_ATI_draw_buffers GL_ATI_texture_float GL_ATI_texture_mirror_once GL_EXTX_framebuffer_mixed_formats GL_EXT_Cg_shader GL_EXT_abgr GL_EXT_bgra GL_EXT_bindable_uniform GL_EXT_blend_color GL_EXT_blend_equation_separate GL_EXT_blend_func_separate GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_compiled_vertex_array GL_EXT_depth_bounds_test GL_EXT_direct_state_access GL_EXT_draw_buffers2 GL_EXT_draw_instanced GL_EXT_draw_range_elements GL_EXT_fog_coord GL_EXT_framebuffer_blit GL_EXT_framebuffer_multisample GL_EXT_framebuffer_multisample_blit_scaled GL_EXT_framebuffer_object GL_EXT_framebuffer_sRGB GL_EXT_geometry_shader4 GL_EXT_gpu_program_parameters GL_EXT_gpu_shader4 GL_EXT_import_sync_object GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_multi_draw_arrays GL_EXT_multiview_texture_multisample GL_EXT_multiview_timer_query GL_EXT_packed_depth_stencil GL_EXT_packed_float GL_EXT_packed_pixels GL_EXT_pixel_buffer_object GL_EXT_point_parameters GL_EXT_polygon_offset_clamp GL_EXT_post_depth_coverage GL_EXT_provoking_vertex GL_EXT_raster_multisample GL_EXT_rescale_normal GL_EXT_secondary_color GL_EXT_semaphore GL_EXT_semaphore_fd GL_EXT_separate_shader_objects GL_EXT_separate_specular_color GL_EXT_shader_image_load_formatted GL_EXT_shader_image_load_store GL_EXT_shader_integer_mix GL_EXT_shadow_funcs GL_EXT_sparse_texture2 GL_EXT_stencil_two_side GL_EXT_stencil_wrap GL_EXT_texture3D GL_EXT_texture_array GL_EXT_texture_buffer_object GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_latc GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map GL_EXT_texture_edge_clamp GL_EXT_texture_env_add GL_EXT_texture_env_combine GL_EXT_texture_env_dot3 GL_EXT_texture_filter_anisotropic GL_EXT_texture_filter_minmax GL_EXT_texture_integer GL_EXT_texture_lod GL_EXT_texture_lod_bias GL_EXT_texture_mirror_clamp GL_EXT_texture_object GL_EXT_texture_sRGB GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_shared_exponent GL_EXT_texture_storage GL_EXT_texture_swizzle GL_EXT_timer_query GL_EXT_transform_feedback2 GL_EXT_vertex_array GL_EXT_vertex_array_bgra GL_EXT_vertex_attrib_64bit GL_EXT_window_rectangles GL_EXT_x11_sync_object GL_IBM_rasterpos_clip GL_IBM_texture_mirrored_repeat GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent GL_KHR_context_flush_control GL_KHR_debug GL_KHR_no_error GL_KHR_parallel_shader_compile GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_shader_subgroup GL_KTX_buffer_region GL_NVX_blend_equation_advanced_multi_draw_buffers GL_NVX_conditional_render GL_NVX_gpu_memory_info GL_NVX_nvenc_interop GL_NVX_progress_fence GL_NV_ES1_1_compatibility GL_NV_ES3_1_compatibility GL_NV_alpha_to_coverage_dither_control GL_NV_bindless_multi_draw_indirect GL_NV_bindless_multi_draw_indirect_count GL_NV_bindless_texture GL_NV_blend_equation_advanced GL_NV_blend_equation_advanced_coherent GL_NV_blend_minmax_factor GL_NV_blend_square GL_NV_clip_space_w_scaling GL_NV_command_list GL_NV_compute_program5 GL_NV_conditional_render GL_NV_conservative_raster GL_NV_conservative_raster_dilate GL_NV_conservative_raster_pre_snap_triangles GL_NV_copy_depth_to_color GL_NV_copy_image GL_NV_depth_buffer_float GL_NV_depth_clamp GL_NV_draw_texture GL_NV_draw_vulkan_image GL_NV_explicit_multisample GL_NV_feature_query GL_NV_fence GL_NV_fill_rectangle GL_NV_float_buffer GL_NV_fog_distance GL_NV_fragment_coverage_to_color GL_NV_fragment_program GL_NV_fragment_program2 GL_NV_fragment_program_option GL_NV_fragment_shader_interlock GL_NV_framebuffer_mixed_samples GL_NV_framebuffer_multisample_coverage GL_NV_geometry_shader4 GL_NV_geometry_shader_passthrough GL_NV_gpu_multicast GL_NV_gpu_program4 GL_NV_gpu_program4_1 GL_NV_gpu_program5 GL_NV_gpu_program5_mem_extended GL_NV_gpu_program_fp64 GL_NV_gpu_program_multiview GL_NV_gpu_shader5 GL_NV_half_float GL_NV_internalformat_sample_query GL_NV_light_max_exponent GL_NV_memory_attachment GL_NV_memory_object_sparse GL_NV_multisample_coverage GL_NV_multisample_filter_hint GL_NV_occlusion_query GL_NV_packed_depth_stencil GL_NV_parameter_buffer_object GL_NV_parameter_buffer_object2 GL_NV_path_rendering GL_NV_path_rendering_shared_edge GL_NV_pixel_data_range GL_NV_point_sprite GL_NV_primitive_restart GL_NV_query_resource GL_NV_query_resource_tag GL_NV_register_combiners GL_NV_register_combiners2 GL_NV_robustness_video_memory_purge GL_NV_sample_locations GL_NV_sample_mask_override_coverage GL_NV_shader_atomic_counters GL_NV_shader_atomic_float GL_NV_shader_atomic_float64 GL_NV_shader_atomic_fp16_vector GL_NV_shader_atomic_int64 GL_NV_shader_buffer_load GL_NV_shader_storage_buffer_object GL_NV_shader_subgroup_partitioned GL_NV_shader_thread_group GL_NV_shader_thread_shuffle GL_NV_stereo_view_rendering GL_NV_texgen_reflection GL_NV_texture_barrier GL_NV_texture_compression_vtc GL_NV_texture_env_combine4 GL_NV_texture_multisample GL_NV_texture_rectangle GL_NV_texture_rectangle_compressed GL_NV_texture_shader GL_NV_texture_shader2 GL_NV_texture_shader3 GL_NV_timeline_semaphore GL_NV_transform_feedback GL_NV_transform_feedback2 GL_NV_uniform_buffer_std430_layout GL_NV_uniform_buffer_unified_memory GL_NV_vdpau_interop GL_NV_vdpau_interop2 GL_NV_vertex_array_range GL_NV_vertex_array_range2 GL_NV_vertex_attrib_integer_64bit GL_NV_vertex_buffer_unified_memory GL_NV_vertex_program GL_NV_vertex_program1_1 GL_NV_vertex_program2 GL_NV_vertex_program2_option GL_NV_vertex_program3 GL_NV_viewport_array2 GL_NV_viewport_swizzle GL_OVR_multiview GL_OVR_multiview2 GL_S3_s3tc GL_SGIS_generate_mipmap GL_SGIS_texture_lod GL_SGIX_depth_texture GL_SGIX_shadow GL_SUN_slice_accum GL_ANDROID_extension_pack_es31a GL_EXT_EGL_image_external_wrap_modes GL_EXT_base_instance GL_EXT_blend_func_extended GL_EXT_blend_minmax GL_EXT_buffer_storage GL_EXT_clear_texture GL_EXT_clip_control GL_EXT_clip_cull_distance GL_EXT_color_buffer_float GL_EXT_color_buffer_half_float GL_EXT_compressed_ETC1_RGB8_sub_texture GL_EXT_conservative_depth GL_EXT_copy_image GL_EXT_debug_label GL_EXT_depth_clamp GL_EXT_discard_framebuffer GL_EXT_disjoint_timer_query GL_EXT_draw_buffers_indexed GL_EXT_draw_elements_base_vertex GL_EXT_draw_transform_feedback GL_EXT_float_blend GL_EXT_frag_depth GL_EXT_geometry_point_size GL_EXT_geometry_shader GL_EXT_gpu_shader5 GL_EXT_map_buffer_range GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_multi_draw_indirect GL_EXT_multisample_compatibility GL_EXT_multisampled_render_to_texture GL_EXT_multisampled_render_to_texture2 GL_EXT_multiview_texture_multisample GL_EXT_multiview_timer_query GL_EXT_occlusion_query_boolean GL_EXT_polygon_offset_clamp GL_EXT_post_depth_coverage GL_EXT_primitive_bounding_box GL_EXT_raster_multisample GL_EXT_read_format_bgra GL_EXT_render_snorm GL_EXT_robustness GL_EXT_sRGB GL_EXT_sRGB_write_control GL_EXT_semaphore GL_EXT_semaphore_fd GL_EXT_separate_shader_objects GL_EXT_shader_group_vote GL_EXT_shader_implicit_conversions GL_EXT_shader_integer_mix GL_EXT_shader_io_blocks GL_EXT_shader_non_constant_global_initializers GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_sparse_texture GL_EXT_sparse_texture2 GL_EXT_tessellation_point_size GL_EXT_tessellation_shader GL_EXT_texture_border_clamp GL_EXT_texture_buffer GL_EXT_texture_compression_bptc GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map_array GL_EXT_texture_filter_anisotropic GL_EXT_texture_filter_minmax GL_EXT_texture_format_BGRA8888 GL_EXT_texture_mirror_clamp_to_edge GL_EXT_texture_norm16 GL_EXT_texture_query_lod GL_EXT_texture_rg GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_storage GL_EXT_texture_view GL_EXT_unpack_subimage GL_EXT_window_rectangles GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent GL_KHR_context_flush_control GL_KHR_debug GL_KHR_no_error GL_KHR_parallel_shader_compile GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_shader_subgroup GL_KHR_texture_compression_astc_hdr GL_KHR_texture_compression_astc_ldr GL_KHR_texture_compression_astc_sliced_3d GL_NVX_blend_equation_advanced_multi_draw_buffers GL_NV_bgr GL_NV_bindless_texture GL_NV_blend_equation_advanced GL_NV_blend_equation_advanced_coherent GL_NV_blend_minmax_factor GL_NV_clip_space_w_scaling GL_NV_conditional_render GL_NV_conservative_raster GL_NV_conservative_raster_pre_snap_triangles GL_NV_copy_buffer GL_NV_copy_image GL_NV_draw_buffers GL_NV_draw_instanced GL_NV_draw_texture GL_NV_draw_vulkan_image GL_NV_explicit_attrib_location GL_NV_fbo_color_attachments GL_NV_fill_rectangle GL_NV_fragment_coverage_to_color GL_NV_fragment_shader_interlock GL_NV_framebuffer_blit GL_NV_framebuffer_mixed_samples GL_NV_framebuffer_multisample GL_NV_generate_mipmap_sRGB GL_NV_geometry_shader_passthrough GL_NV_gpu_shader5 GL_NV_image_formats GL_NV_instanced_arrays GL_NV_internalformat_sample_query GL_NV_memory_attachment GL_NV_memory_object_sparse GL_NV_non_square_matrices GL_NV_occlusion_query_samples GL_NV_pack_subimage GL_NV_packed_float GL_NV_packed_float_linear GL_NV_path_rendering GL_NV_path_rendering_shared_edge GL_NV_pixel_buffer_object GL_NV_polygon_mode GL_NV_read_buffer GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stencil GL_NV_sRGB_formats GL_NV_sample_locations GL_NV_sample_mask_override_coverage GL_NV_shader_atomic_fp16_vector GL_NV_shader_noperspective_interpolation GL_NV_shader_subgroup_partitioned GL_NV_shadow_samplers_array GL_NV_shadow_samplers_cube GL_NV_stereo_view_rendering GL_NV_texture_array GL_NV_texture_barrier GL_NV_texture_border_clamp GL_NV_texture_compression_latc GL_NV_texture_compression_s3tc GL_NV_texture_compression_s3tc_update GL_NV_timeline_semaphore GL_NV_timer_query GL_NV_viewport_array GL_NV_viewport_array2 GL_NV_viewport_swizzle GL_OES_compressed_ETC1_RGB8_texture GL_OES_copy_image GL_OES_depth24 GL_OES_depth32 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_draw_buffers_indexed GL_OES_draw_elements_base_vertex GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_geometry_point_size GL_OES_geometry_shader GL_OES_get_program_binary GL_OES_gpu_shader5 GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_primitive_bounding_box GL_OES_rgb8_rgba8 GL_OES_sample_shading GL_OES_sample_variables GL_OES_shader_image_atomic GL_OES_shader_io_blocks GL_OES_shader_multisample_interpolation GL_OES_standard_derivatives GL_OES_tessellation_point_size GL_OES_tessellation_shader GL_OES_texture_border_clamp GL_OES_texture_buffer GL_OES_texture_cube_map_array GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_texture_stencil8 GL_OES_texture_storage_multisample_2d_array GL_OES_texture_view GL_OES_vertex_array_object GL_OES_vertex_half_float GL_OES_viewport_array GL_OVR_multiview GL_OVR_multiview2 GL_OVR_multiview_multisampled_render_to_texture"
+
+# This was automated via a script so I might have missed some
+ext="GL_AMD_multi_draw_indirect GL_AMD_seamless_cubemap_per_texture GL_AMD_vertex_shader_layer GL_AMD_vertex_shader_viewport_index"
+ext="$ext GL_ARB_ES2_compatibility GL_ARB_ES3_1_compatibility GL_ARB_ES3_2_compatibility GL_ARB_ES3_compatibility GL_ARB_arrays_of_arrays"
+ext="$ext GL_ARB_base_instance GL_ARB_bindless_texture GL_ARB_blend_func_extended GL_ARB_buffer_storage GL_ARB_clear_buffer_object GL_ARB_clear_texture"
+ext="$ext GL_ARB_clip_control GL_ARB_color_buffer_float GL_ARB_compressed_texture_pixel_storage GL_ARB_compute_shader GL_ARB_compute_variable_group_size"
+ext="$ext GL_ARB_conditional_render_inverted GL_ARB_conservative_depth GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance GL_ARB_debug_output"
+ext="$ext GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers"
+ext="$ext GL_ARB_draw_buffers_blend GL_ARB_draw_elements_base_vertex GL_ARB_draw_indirect GL_ARB_draw_instanced GL_ARB_enhanced_layouts"
+ext="$ext GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions GL_ARB_fragment_layer_viewport"
+ext="$ext GL_ARB_fragment_program GL_ARB_fragment_program_shadow GL_ARB_fragment_shader GL_ARB_fragment_shader_interlock GL_ARB_framebuffer_no_attachments"
+ext="$ext GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary GL_ARB_get_texture_sub_image GL_ARB_gl_spirv"
+ext="$ext GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64 GL_ARB_gpu_shader_int64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_imaging"
+ext="$ext GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2 GL_ARB_invalidate_subdata"
+ext="$ext GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture"
+ext="$ext GL_ARB_occlusion_query GL_ARB_occlusion_query2 GL_ARB_parallel_shader_compile GL_ARB_pipeline_statistics_query GL_ARB_pixel_buffer_object"
+ext="$ext GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_polygon_offset_clamp GL_ARB_post_depth_coverage GL_ARB_program_interface_query"
+ext="$ext GL_ARB_provoking_vertex GL_ARB_query_buffer_object GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_sample_locations"
+ext="$ext GL_ARB_sample_shading GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_seamless_cubemap_per_texture GL_ARB_separate_shader_objects"
+ext="$ext GL_ARB_shader_atomic_counter_ops GL_ARB_shader_atomic_counters GL_ARB_shader_ballot GL_ARB_shader_bit_encoding GL_ARB_shader_clock"
+ext="$ext GL_ARB_shader_draw_parameters GL_ARB_shader_group_vote GL_ARB_shader_image_load_store GL_ARB_shader_image_size GL_ARB_shader_objects"
+ext="$ext GL_ARB_shader_precision GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine GL_ARB_shader_texture_image_samples"
+ext="$ext GL_ARB_shader_texture_lod GL_ARB_shader_viewport_layer_array GL_ARB_shading_language_100 GL_ARB_shading_language_420pack"
+ext="$ext GL_ARB_shading_language_include GL_ARB_shading_language_packing GL_ARB_shadow GL_ARB_sparse_buffer GL_ARB_sparse_texture GL_ARB_sparse_texture2"
+ext="$ext GL_ARB_sparse_texture_clamp GL_ARB_spirv_extensions GL_ARB_stencil_texturing GL_ARB_sync GL_ARB_tessellation_shader GL_ARB_texture_barrier"
+ext="$ext GL_ARB_texture_border_clamp GL_ARB_texture_buffer_object GL_ARB_texture_buffer_object_rgb32 GL_ARB_texture_buffer_range GL_ARB_texture_compression"
+ext="$ext GL_ARB_texture_compression_bptc GL_ARB_texture_compression_rgtc GL_ARB_texture_cube_map GL_ARB_texture_cube_map_array GL_ARB_texture_env_add"
+ext="$ext GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_filter_anisotropic GL_ARB_texture_filter_minmax"
+ext="$ext GL_ARB_texture_float GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample"
+ext="$ext GL_ARB_texture_non_power_of_two GL_ARB_texture_query_levels GL_ARB_texture_query_lod GL_ARB_texture_rectangle GL_ARB_texture_rg"
+ext="$ext GL_ARB_texture_rgb10_a2ui GL_ARB_texture_stencil8 GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_swizzle"
+ext="$ext GL_ARB_texture_view GL_ARB_timer_query GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 GL_ARB_transform_feedback_instanced"
+ext="$ext GL_ARB_transform_feedback_overflow_query GL_ARB_transpose_matrix GL_ARB_uniform_buffer_object GL_ARB_vertex_array_bgra"
+ext="$ext GL_ARB_vertex_array_object GL_ARB_vertex_attrib_64bit GL_ARB_vertex_attrib_binding GL_ARB_vertex_buffer_object"
+ext="$ext GL_ARB_vertex_program GL_ARB_vertex_shader GL_ARB_vertex_type_10f_11f_11f_rev GL_ARB_vertex_type_2_10_10_10_rev"
+ext="$ext GL_ARB_viewport_array GL_ARB_window_pos GL_ATI_draw_buffers GL_ATI_texture_float GL_ATI_texture_mirror_once"
+ext="$ext GL_EXTX_framebuffer_mixed_formats GL_EXT_Cg_shader GL_EXT_abgr GL_EXT_bgra GL_EXT_bindable_uniform"
+ext="$ext GL_EXT_blend_color GL_EXT_blend_equation_separate GL_EXT_blend_func_separate GL_EXT_blend_minmax"
+ext="$ext GL_EXT_blend_subtract GL_EXT_compiled_vertex_array GL_EXT_depth_bounds_test GL_EXT_direct_state_access"
+ext="$ext GL_EXT_draw_buffers2 GL_EXT_draw_instanced GL_EXT_draw_range_elements GL_EXT_fog_coord GL_EXT_framebuffer_blit"
+ext="$ext GL_EXT_framebuffer_multisample GL_EXT_framebuffer_multisample_blit_scaled GL_EXT_framebuffer_object"
+ext="$ext GL_EXT_framebuffer_sRGB GL_EXT_geometry_shader4 GL_EXT_gpu_program_parameters GL_EXT_gpu_shader4"
+ext="$ext GL_EXT_import_sync_object GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_multi_draw_arrays"
+ext="$ext GL_EXT_multiview_texture_multisample GL_EXT_multiview_timer_query GL_EXT_packed_depth_stencil"
+ext="$ext GL_EXT_packed_float GL_EXT_packed_pixels GL_EXT_pixel_buffer_object GL_EXT_point_parameters"
+ext="$ext GL_EXT_polygon_offset_clamp GL_EXT_post_depth_coverage GL_EXT_provoking_vertex GL_EXT_raster_multisample"
+ext="$ext GL_EXT_rescale_normal GL_EXT_secondary_color GL_EXT_semaphore GL_EXT_semaphore_fd"
+ext="$ext GL_EXT_separate_shader_objects GL_EXT_separate_specular_color GL_EXT_shader_image_load_formatted"
+ext="$ext GL_EXT_shader_image_load_store GL_EXT_shader_integer_mix GL_EXT_shadow_funcs GL_EXT_sparse_texture2"
+ext="$ext GL_EXT_stencil_two_side GL_EXT_stencil_wrap GL_EXT_texture3D GL_EXT_texture_array"
+ext="$ext GL_EXT_texture_buffer_object GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_latc"
+ext="$ext GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map"
+ext="$ext GL_EXT_texture_edge_clamp GL_EXT_texture_env_add GL_EXT_texture_env_combine GL_EXT_texture_env_dot3"
+ext="$ext GL_EXT_texture_filter_anisotropic GL_EXT_texture_filter_minmax GL_EXT_texture_integer"
+ext="$ext GL_EXT_texture_lod GL_EXT_texture_lod_bias GL_EXT_texture_mirror_clamp GL_EXT_texture_object"
+ext="$ext GL_EXT_texture_sRGB GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod"
+ext="$ext GL_EXT_texture_shared_exponent GL_EXT_texture_storage GL_EXT_texture_swizzle GL_EXT_timer_query"
+ext="$ext GL_EXT_transform_feedback2 GL_EXT_vertex_array GL_EXT_vertex_array_bgra GL_EXT_vertex_attrib_64bit"
+ext="$ext GL_EXT_window_rectangles GL_EXT_x11_sync_object GL_IBM_rasterpos_clip GL_IBM_texture_mirrored_repeat"
+ext="$ext GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent GL_KHR_context_flush_control"
+ext="$ext GL_KHR_debug GL_KHR_no_error GL_KHR_parallel_shader_compile GL_KHR_robust_buffer_access_behavior"
+ext="$ext GL_KHR_robustness GL_KHR_shader_subgroup GL_KTX_buffer_region GL_NVX_blend_equation_advanced_multi_draw_buffers"
+ext="$ext GL_NVX_conditional_render GL_NVX_gpu_memory_info GL_NVX_nvenc_interop GL_NVX_progress_fence"
+ext="$ext GL_NV_ES1_1_compatibility GL_NV_ES3_1_compatibility GL_NV_alpha_to_coverage_dither_control"
+ext="$ext GL_NV_bindless_multi_draw_indirect GL_NV_bindless_multi_draw_indirect_count GL_NV_bindless_texture"
+ext="$ext GL_NV_blend_equation_advanced GL_NV_blend_equation_advanced_coherent GL_NV_blend_minmax_factor"
+ext="$ext GL_NV_blend_square GL_NV_clip_space_w_scaling GL_NV_command_list GL_NV_compute_program5"
+ext="$ext GL_NV_conditional_render GL_NV_conservative_raster GL_NV_conservative_raster_dilate"
+ext="$ext GL_NV_conservative_raster_pre_snap_triangles GL_NV_copy_depth_to_color GL_NV_copy_image"
+ext="$ext GL_NV_depth_buffer_float GL_NV_depth_clamp GL_NV_draw_texture GL_NV_draw_vulkan_image"
+ext="$ext GL_NV_explicit_multisample GL_NV_feature_query GL_NV_fence GL_NV_fill_rectangle"
+ext="$ext GL_NV_float_buffer GL_NV_fog_distance GL_NV_fragment_coverage_to_color GL_NV_fragment_program"
+ext="$ext GL_NV_fragment_program2 GL_NV_fragment_program_option GL_NV_fragment_shader_interlock"
+ext="$ext GL_NV_framebuffer_mixed_samples GL_NV_framebuffer_multisample_coverage GL_NV_geometry_shader4"
+ext="$ext GL_NV_geometry_shader_passthrough GL_NV_gpu_multicast GL_NV_gpu_program4 GL_NV_gpu_program4_1"
+ext="$ext GL_NV_gpu_program5 GL_NV_gpu_program5_mem_extended GL_NV_gpu_program_fp64 GL_NV_gpu_program_multiview"
+ext="$ext GL_NV_gpu_shader5 GL_NV_half_float GL_NV_internalformat_sample_query GL_NV_light_max_exponent"
+ext="$ext GL_NV_memory_attachment GL_NV_memory_object_sparse GL_NV_multisample_coverage GL_NV_multisample_filter_hint"
+ext="$ext GL_NV_occlusion_query GL_NV_packed_depth_stencil GL_NV_parameter_buffer_object GL_NV_parameter_buffer_object2"
+ext="$ext GL_NV_path_rendering GL_NV_path_rendering_shared_edge GL_NV_pixel_data_range GL_NV_point_sprite"
+ext="$ext GL_NV_primitive_restart GL_NV_query_resource GL_NV_query_resource_tag GL_NV_register_combiners"
+ext="$ext GL_NV_register_combiners2 GL_NV_robustness_video_memory_purge GL_NV_sample_locations"
+ext="$ext GL_NV_sample_mask_override_coverage GL_NV_shader_atomic_counters GL_NV_shader_atomic_float"
+ext="$ext GL_NV_shader_atomic_float64 GL_NV_shader_atomic_fp16_vector GL_NV_shader_atomic_int64"
+ext="$ext GL_NV_shader_buffer_load GL_NV_shader_storage_buffer_object GL_NV_shader_subgroup_partitioned"
+ext="$ext GL_NV_shader_thread_group GL_NV_shader_thread_shuffle GL_NV_stereo_view_rendering GL_NV_texgen_reflection"
+ext="$ext GL_NV_texture_barrier GL_NV_texture_compression_vtc GL_NV_texture_env_combine4 GL_NV_texture_multisample"
+ext="$ext GL_NV_texture_rectangle GL_NV_texture_rectangle_compressed GL_NV_texture_shader GL_NV_texture_shader2"
+ext="$ext GL_NV_texture_shader3 GL_NV_timeline_semaphore GL_NV_transform_feedback GL_NV_transform_feedback2"
+ext="$ext GL_NV_uniform_buffer_std430_layout GL_NV_uniform_buffer_unified_memory GL_NV_vdpau_interop"
+ext="$ext GL_NV_vdpau_interop2 GL_NV_vertex_array_range GL_NV_vertex_array_range2 GL_NV_vertex_attrib_integer_64bit"
+ext="$ext GL_NV_vertex_buffer_unified_memory GL_NV_vertex_program GL_NV_vertex_program1_1 GL_NV_vertex_program2"
+ext="$ext GL_NV_vertex_program2_option GL_NV_vertex_program3 GL_NV_viewport_array2 GL_NV_viewport_swizzle"
+ext="$ext GL_OVR_multiview GL_OVR_multiview2 GL_S3_s3tc GL_SGIS_generate_mipmap GL_SGIS_texture_lod"
+ext="$ext GL_SGIX_depth_texture GL_SGIX_shadow GL_SUN_slice_accum GL_AMD_multi_draw_indirect"
+ext="$ext GL_AMD_seamless_cubemap_per_texture GL_AMD_vertex_shader_layer GL_AMD_vertex_shader_viewport_index"
+ext="$ext GL_ARB_ES2_compatibility GL_ARB_ES3_1_compatibility GL_ARB_ES3_2_compatibility GL_ARB_ES3_compatibility"
+ext="$ext GL_ARB_arrays_of_arrays GL_ARB_base_instance GL_ARB_bindless_texture GL_ARB_blend_func_extended"
+ext="$ext GL_ARB_buffer_storage GL_ARB_clear_buffer_object GL_ARB_clear_texture GL_ARB_clip_control"
+ext="$ext GL_ARB_color_buffer_float GL_ARB_compatibility GL_ARB_compressed_texture_pixel_storage"
+ext="$ext GL_ARB_compute_shader GL_ARB_compute_variable_group_size GL_ARB_conditional_render_inverted"
+ext="$ext GL_ARB_conservative_depth GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance"
+ext="$ext GL_ARB_debug_output GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture"
+ext="$ext GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers GL_ARB_draw_buffers_blend"
+ext="$ext GL_ARB_draw_elements_base_vertex GL_ARB_draw_indirect GL_ARB_draw_instanced GL_ARB_enhanced_layouts"
+ext="$ext GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions"
+ext="$ext GL_ARB_fragment_layer_viewport GL_ARB_fragment_program GL_ARB_fragment_program_shadow"
+ext="$ext GL_ARB_fragment_shader GL_ARB_fragment_shader_interlock GL_ARB_framebuffer_no_attachments"
+ext="$ext GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary"
+ext="$ext GL_ARB_get_texture_sub_image GL_ARB_gl_spirv GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64"
+ext="$ext GL_ARB_gpu_shader_int64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_imaging"
+ext="$ext GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2"
+ext="$ext GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind"
+ext="$ext GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture GL_ARB_occlusion_query"
+ext="$ext GL_ARB_occlusion_query2 GL_ARB_parallel_shader_compile GL_ARB_pipeline_statistics_query"
+ext="$ext GL_ARB_pixel_buffer_object GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_polygon_offset_clamp"
+ext="$ext GL_ARB_post_depth_coverage GL_ARB_program_interface_query GL_ARB_provoking_vertex GL_ARB_query_buffer_object"
+ext="$ext GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_sample_locations GL_ARB_sample_shading"
+ext="$ext GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_seamless_cubemap_per_texture"
+ext="$ext GL_ARB_separate_shader_objects GL_ARB_shader_atomic_counter_ops GL_ARB_shader_atomic_counters"
+ext="$ext GL_ARB_shader_ballot GL_ARB_shader_bit_encoding GL_ARB_shader_clock GL_ARB_shader_draw_parameters"
+ext="$ext GL_ARB_shader_group_vote GL_ARB_shader_image_load_store GL_ARB_shader_image_size GL_ARB_shader_objects"
+ext="$ext GL_ARB_shader_precision GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine"
+ext="$ext GL_ARB_shader_texture_image_samples GL_ARB_shader_texture_lod GL_ARB_shader_viewport_layer_array"
+ext="$ext GL_ARB_shading_language_100 GL_ARB_shading_language_420pack GL_ARB_shading_language_include"
+ext="$ext GL_ARB_shading_language_packing GL_ARB_shadow GL_ARB_sparse_buffer GL_ARB_sparse_texture"
+ext="$ext GL_ARB_sparse_texture2 GL_ARB_sparse_texture_clamp GL_ARB_spirv_extensions GL_ARB_stencil_texturing"
+ext="$ext GL_ARB_sync GL_ARB_tessellation_shader GL_ARB_texture_barrier GL_ARB_texture_border_clamp"
+ext="$ext GL_ARB_texture_buffer_object GL_ARB_texture_buffer_object_rgb32 GL_ARB_texture_buffer_range"
+ext="$ext GL_ARB_texture_compression GL_ARB_texture_compression_bptc GL_ARB_texture_compression_rgtc"
+ext="$ext GL_ARB_texture_cube_map GL_ARB_texture_cube_map_array GL_ARB_texture_env_add GL_ARB_texture_env_combine"
+ext="$ext GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_filter_anisotropic"
+ext="$ext GL_ARB_texture_filter_minmax GL_ARB_texture_float GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge"
+ext="$ext GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample GL_ARB_texture_non_power_of_two"
+ext="$ext GL_ARB_texture_query_levels GL_ARB_texture_query_lod GL_ARB_texture_rectangle GL_ARB_texture_rg"
+ext="$ext GL_ANDROID_extension_pack_es31a GL_EXT_EGL_image_external_wrap_modes GL_EXT_base_instance"
+ext="$ext GL_EXT_blend_func_extended GL_EXT_blend_minmax GL_EXT_buffer_storage GL_EXT_clear_texture"
+ext="$ext GL_EXT_clip_control GL_EXT_clip_cull_distance GL_EXT_color_buffer_float GL_EXT_color_buffer_half_float"
+ext="$ext GL_EXT_compressed_ETC1_RGB8_sub_texture GL_EXT_conservative_depth GL_EXT_copy_image GL_EXT_debug_label"
+ext="$ext GL_EXT_depth_clamp GL_EXT_discard_framebuffer GL_EXT_disjoint_timer_query GL_EXT_draw_buffers_indexed"
+ext="$ext GL_EXT_draw_elements_base_vertex GL_EXT_draw_transform_feedback GL_EXT_float_blend GL_EXT_frag_depth"
+ext="$ext GL_EXT_geometry_point_size GL_EXT_geometry_shader GL_EXT_gpu_shader5 GL_EXT_map_buffer_range"
+ext="$ext GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_multi_draw_indirect GL_EXT_multisample_compatibility"
+ext="$ext GL_EXT_multisampled_render_to_texture GL_EXT_multisampled_render_to_texture2 GL_EXT_multiview_texture_multisample"
+ext="$ext GL_EXT_multiview_timer_query GL_EXT_occlusion_query_boolean GL_EXT_polygon_offset_clamp"
+ext="$ext GL_EXT_post_depth_coverage GL_EXT_primitive_bounding_box GL_EXT_raster_multisample GL_EXT_read_format_bgra"
+ext="$ext GL_EXT_render_snorm GL_EXT_robustness GL_EXT_sRGB GL_EXT_sRGB_write_control GL_EXT_semaphore"
+ext="$ext GL_EXT_semaphore_fd GL_EXT_separate_shader_objects GL_EXT_shader_group_vote GL_EXT_shader_implicit_conversions"
+ext="$ext GL_EXT_shader_integer_mix GL_EXT_shader_io_blocks GL_EXT_shader_non_constant_global_initializers"
+ext="$ext GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_sparse_texture GL_EXT_sparse_texture2"
+ext="$ext GL_EXT_tessellation_point_size GL_EXT_tessellation_shader GL_EXT_texture_border_clamp GL_EXT_texture_buffer"
+ext="$ext GL_EXT_texture_compression_bptc GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_rgtc"
+ext="$ext GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map_array GL_EXT_texture_filter_anisotropic"
+ext="$ext GL_EXT_texture_filter_minmax GL_EXT_texture_format_BGRA8888 GL_EXT_texture_mirror_clamp_to_edge"
+ext="$ext GL_EXT_texture_norm16 GL_EXT_texture_query_lod GL_EXT_texture_rg GL_EXT_texture_sRGB_R8"
+ext="$ext GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_storage GL_EXT_texture_view"
+ext="$ext GL_EXT_unpack_subimage GL_EXT_window_rectangles GL_KHR_blend_equation_advanced GL_KHR_blend_equation_advanced_coherent"
+ext="$ext GL_KHR_context_flush_control GL_KHR_debug GL_KHR_no_error GL_KHR_parallel_shader_compile"
+ext="$ext GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_shader_subgroup GL_KHR_texture_compression_astc_hdr"
+ext="$ext GL_KHR_texture_compression_astc_ldr GL_KHR_texture_compression_astc_sliced_3d GL_NVX_blend_equation_advanced_multi_draw_buffers"
+ext="$ext GL_NV_bgr GL_NV_bindless_texture GL_NV_blend_equation_advanced GL_NV_blend_equation_advanced_coherent"
+ext="$ext GL_NV_blend_minmax_factor GL_NV_clip_space_w_scaling GL_NV_conditional_render GL_NV_conservative_raster"
+ext="$ext GL_NV_conservative_raster_pre_snap_triangles GL_NV_copy_buffer GL_NV_copy_image GL_NV_draw_buffers"
+ext="$ext GL_NV_draw_instanced GL_NV_draw_texture GL_NV_draw_vulkan_image GL_NV_explicit_attrib_location"
+ext="$ext GL_NV_fbo_color_attachments GL_NV_fill_rectangle GL_NV_fragment_coverage_to_color GL_NV_fragment_shader_interlock"
+ext="$ext GL_NV_framebuffer_blit GL_NV_framebuffer_mixed_samples GL_NV_framebuffer_multisample GL_NV_generate_mipmap_sRGB"
+ext="$ext GL_NV_geometry_shader_passthrough GL_NV_gpu_shader5 GL_NV_image_formats GL_NV_instanced_arrays"
+ext="$ext GL_NV_internalformat_sample_query GL_NV_memory_attachment GL_NV_memory_object_sparse GL_NV_non_square_matrices"
+ext="$ext GL_NV_occlusion_query_samples GL_NV_pack_subimage GL_NV_packed_float GL_NV_packed_float_linear"
+ext="$ext GL_NV_path_rendering GL_NV_path_rendering_shared_edge GL_NV_pixel_buffer_object GL_NV_polygon_mode"
+ext="$ext GL_NV_read_buffer GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stencil GL_NV_sRGB_formats"
+ext="$ext GL_NV_sample_locations GL_NV_sample_mask_override_coverage GL_NV_shader_atomic_fp16_vector"
+ext="$ext GL_NV_shader_noperspective_interpolation GL_NV_shader_subgroup_partitioned GL_NV_shadow_samplers_array"
+ext="$ext GL_NV_shadow_samplers_cube GL_NV_stereo_view_rendering GL_NV_texture_array GL_NV_texture_barrier"
+ext="$ext GL_NV_texture_border_clamp GL_NV_texture_compression_latc GL_NV_texture_compression_s3tc GL_NV_texture_compression_s3tc_update"
+ext="$ext GL_NV_timeline_semaphore GL_NV_timer_query GL_NV_viewport_array GL_NV_viewport_array2 GL_NV_viewport_swizzle"
+ext="$ext GL_OES_compressed_ETC1_RGB8_texture GL_OES_copy_image GL_OES_depth24 GL_OES_depth32 GL_OES_depth_texture"
+ext="$ext GL_OES_depth_texture_cube_map GL_OES_draw_buffers_indexed GL_OES_draw_elements_base_vertex GL_OES_element_index_uint"
+ext="$ext GL_OES_fbo_render_mipmap GL_OES_geometry_point_size GL_OES_geometry_shader GL_OES_get_program_binary"
+ext="$ext GL_OES_gpu_shader5 GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_primitive_bounding_box"
+ext="$ext GL_OES_rgb8_rgba8 GL_OES_sample_shading GL_OES_sample_variables GL_OES_shader_image_atomic"
+ext="$ext GL_OES_shader_io_blocks GL_OES_shader_multisample_interpolation GL_OES_standard_derivatives GL_OES_tessellation_point_size"
+ext="$ext GL_OES_tessellation_shader GL_OES_texture_border_clamp GL_OES_texture_buffer GL_OES_texture_cube_map_array"
+ext="$ext GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear"
+ext="$ext GL_OES_texture_npot GL_OES_texture_stencil8 GL_OES_texture_storage_multisample_2d_array GL_OES_texture_view"
+ext="$ext GL_OES_vertex_array_object GL_OES_vertex_half_float GL_OES_viewport_array GL_OVR_multiview GL_OVR_multiview2"
+ext="$ext GL_OVR_multiview_multisampled_render_to_texture"
+
+export MESA_EXTENSION_OVERRIDE="$ext"
"$@"
diff --git a/tools/optimize-assets.sh b/tools/optimize-assets.sh
index 07facc8fa0..b7d52330f2 100755
--- a/tools/optimize-assets.sh
+++ b/tools/optimize-assets.sh
@@ -1,6 +1,9 @@
#!/bin/sh -e
+
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
-# Optimizes assets of eden (requires OptiPng)
+
+# Optimizes assets of Eden (requires OptiPng)
+
which optipng || exit
-find . -type f -name *.png -exec optipng -o7 {} \;
+find . -type f -name "*.png" -exec optipng -o7 {} \;
diff --git a/tools/reset-submodules.sh b/tools/reset-submodules.sh
deleted file mode 100755
index 6fdfe0bcdb..0000000000
--- a/tools/reset-submodules.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash -ex
-
-# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
-# SPDX-License-Identifier: MIT
-
-git submodule sync
-git submodule foreach --recursive git reset --hard
-git submodule update --init --recursive
diff --git a/tools/shellcheck.sh b/tools/shellcheck.sh
new file mode 100755
index 0000000000..719c717cf2
--- /dev/null
+++ b/tools/shellcheck.sh
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# fd is slightly faster on NVMe (the syntax sux though)
+if command -v fd > /dev/null; then
+ fd . tools -esh -x shellcheck
+else
+ find tools -name "*.sh" -exec shellcheck -s sh {} \;
+fi
\ No newline at end of file
diff --git a/tools/update-cpm.sh b/tools/update-cpm.sh
index 30e400209d..8bd8df2b83 100755
--- a/tools/update-cpm.sh
+++ b/tools/update-cpm.sh
@@ -1,3 +1,6 @@
-#!/bin/sh
+#!/bin/sh -e
-wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/CPM.cmake
diff --git a/tools/update-icons.sh b/tools/update-icons.sh
index da54156665..a2c1ae8ebf 100755
--- a/tools/update-icons.sh
+++ b/tools/update-icons.sh
@@ -1,8 +1,11 @@
#!/bin/sh -e
+
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
+
# Updates main icons for eden
-which png2icns || [ which yay && yay libicns ] || exit
+
+which png2icns || (which yay && yay libicns) || exit
which magick || exit
export EDEN_SVG_ICO="dist/dev.eden_emu.eden.svg"
diff --git a/tools/url-hash.sh b/tools/url-hash.sh
deleted file mode 100755
index a54dec8bb2..0000000000
--- a/tools/url-hash.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-SUM=`wget -q $1 -O - | sha512sum`
-echo "$SUM" | cut -d " " -f1