summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/emu_window.h6
-rw-r--r--src/core/hle/service/audio/audin_u.cpp5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp15
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp76
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h10
-rw-r--r--src/yuzu/bootmanager.cpp12
-rw-r--r--src/yuzu/main.cpp13
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp2
23 files changed, 173 insertions, 76 deletions
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 95363b645..cf85ba29e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -131,6 +131,10 @@ public:
131 return active_config; 131 return active_config;
132 } 132 }
133 133
134 bool StrictContextRequired() const {
135 return strict_context_required;
136 }
137
134 /** 138 /**
135 * Requests the internal configuration to be replaced by the specified argument at some point in 139 * Requests the internal configuration to be replaced by the specified argument at some point in
136 * the future. 140 * the future.
@@ -207,6 +211,8 @@ protected:
207 211
208 WindowSystemInfo window_info; 212 WindowSystemInfo window_info;
209 213
214 bool strict_context_required = false;
215
210private: 216private:
211 /** 217 /**
212 * Handler called when the minimal client area was requested to be changed via SetConfig. 218 * Handler called when the minimal client area was requested to be changed via SetConfig.
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 26dec7147..053e8f9dd 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -203,8 +203,9 @@ private:
203}; 203};
204 204
205AudInU::AudInU(Core::System& system_) 205AudInU::AudInU(Core::System& system_)
206 : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, 206 : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
207 impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { 207 service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
208 system_)} {
208 // clang-format off 209 // clang-format off
209 static const FunctionInfo functions[] = { 210 static const FunctionInfo functions[] = {
210 {0, &AudInU::ListAudioIns, "ListAudioIns"}, 211 {0, &AudInU::ListAudioIns, "ListAudioIns"},
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 991e30ba1..29751f075 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -26,8 +26,9 @@ public:
26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
27 size_t session_id, const std::string& device_name, 27 size_t session_id, const std::string& device_name,
28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
29 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, 29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
30 event{service_context.CreateEvent("AudioOutEvent")}, 30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
31 "AudioOutEvent")},
31 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { 32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
32 33
33 // clang-format off 34 // clang-format off
@@ -220,8 +221,9 @@ private:
220}; 221};
221 222
222AudOutU::AudOutU(Core::System& system_) 223AudOutU::AudOutU(Core::System& system_)
223 : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, 224 : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
224 impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { 225 service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
226 system_)} {
225 // clang-format off 227 // clang-format off
226 static const FunctionInfo functions[] = { 228 static const FunctionInfo functions[] = {
227 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, 229 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index ead16c321..3a1c231b6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -35,9 +35,10 @@ public:
35 AudioCore::AudioRendererParameterInternal& params, 35 AudioCore::AudioRendererParameterInternal& params,
36 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, 36 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
37 u32 process_handle, u64 applet_resource_user_id, s32 session_id) 37 u32 process_handle, u64 applet_resource_user_id, s32 session_id)
38 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, 38 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
39 rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, 39 service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
40 impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { 40 "IAudioRendererEvent")},
41 manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
41 // clang-format off 42 // clang-format off
42 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
43 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 44 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
@@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
242public: 243public:
243 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, 244 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
244 u32 device_num) 245 u32 device_num)
245 : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, 246 : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
246 impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, 247 service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
248 system_, applet_resource_user_id,
249 revision)},
247 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { 250 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
248 static const FunctionInfo functions[] = { 251 static const FunctionInfo functions[] = {
249 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, 252 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
@@ -418,7 +421,7 @@ private:
418}; 421};
419 422
420AudRenU::AudRenU(Core::System& system_) 423AudRenU::AudRenU(Core::System& system_)
421 : ServiceFramework{system_, "audren:u"}, 424 : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
422 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { 425 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
423 // clang-format off 426 // clang-format off
424 static const FunctionInfo functions[] = { 427 static const FunctionInfo functions[] = {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 28b38273e..c6d54be63 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -223,8 +223,6 @@ struct GPU::Impl {
223 /// core timing events. 223 /// core timing events.
224 void Start() { 224 void Start() {
225 gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); 225 gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
226 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
227 cpu_context->MakeCurrent();
228 } 226 }
229 227
230 void NotifyShutdown() { 228 void NotifyShutdown() {
@@ -235,6 +233,9 @@ struct GPU::Impl {
235 233
236 /// Obtain the CPU Context 234 /// Obtain the CPU Context
237 void ObtainContext() { 235 void ObtainContext() {
236 if (!cpu_context) {
237 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
238 }
238 cpu_context->MakeCurrent(); 239 cpu_context->MakeCurrent();
239 } 240 }
240 241
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index e2e3dac34..cee5c3247 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -112,7 +112,7 @@ bool IsASTCSupported() {
112} 112}
113} // Anonymous namespace 113} // Anonymous namespace
114 114
115Device::Device() { 115Device::Device(Core::Frontend::EmuWindow& emu_window) {
116 if (!GLAD_GL_VERSION_4_6) { 116 if (!GLAD_GL_VERSION_4_6) {
117 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); 117 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available");
118 throw std::runtime_error{"Insufficient version"}; 118 throw std::runtime_error{"Insufficient version"};
@@ -126,9 +126,9 @@ Device::Device() {
126 const bool is_intel = vendor_name == "Intel"; 126 const bool is_intel = vendor_name == "Intel";
127 127
128#ifdef __unix__ 128#ifdef __unix__
129 const bool is_linux = true; 129 constexpr bool is_linux = true;
130#else 130#else
131 const bool is_linux = false; 131 constexpr bool is_linux = false;
132#endif 132#endif
133 133
134 bool disable_fast_buffer_sub_data = false; 134 bool disable_fast_buffer_sub_data = false;
@@ -193,9 +193,11 @@ Device::Device() {
193 } 193 }
194 } 194 }
195 195
196 strict_context_required = emu_window.StrictContextRequired();
196 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. 197 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
198 // Blocks EGL on Wayland from using asynchronous shader compilation.
197 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && 199 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() &&
198 !(is_amd || (is_intel && !is_linux)); 200 !(is_amd || (is_intel && !is_linux)) && !strict_context_required;
199 use_driver_cache = is_nvidia; 201 use_driver_cache = is_nvidia;
200 202
201 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 203 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 5ef51ebcf..2a72d84be 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -5,6 +5,7 @@
5 5
6#include <cstddef> 6#include <cstddef>
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/frontend/emu_window.h"
8#include "shader_recompiler/stage.h" 9#include "shader_recompiler/stage.h"
9 10
10namespace Settings { 11namespace Settings {
@@ -15,7 +16,7 @@ namespace OpenGL {
15 16
16class Device { 17class Device {
17public: 18public:
18 explicit Device(); 19 explicit Device(Core::Frontend::EmuWindow& emu_window);
19 20
20 [[nodiscard]] std::string GetVendorName() const; 21 [[nodiscard]] std::string GetVendorName() const;
21 22
@@ -173,6 +174,10 @@ public:
173 return can_report_memory; 174 return can_report_memory;
174 } 175 }
175 176
177 bool StrictContextRequired() const {
178 return strict_context_required;
179 }
180
176private: 181private:
177 static bool TestVariableAoffi(); 182 static bool TestVariableAoffi();
178 static bool TestPreciseBug(); 183 static bool TestPreciseBug();
@@ -216,6 +221,7 @@ private:
216 bool has_cbuf_ftou_bug{}; 221 bool has_cbuf_ftou_bug{};
217 bool has_bool_ref_bug{}; 222 bool has_bool_ref_bug{};
218 bool can_report_memory{}; 223 bool can_report_memory{};
224 bool strict_context_required{};
219 225
220 std::string vendor_name; 226 std::string vendor_name;
221}; 227};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index a59d0d24e..fff55d585 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -174,6 +174,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
174 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, 174 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
175 state_tracker{state_tracker_}, shader_notify{shader_notify_}, 175 state_tracker{state_tracker_}, shader_notify{shader_notify_},
176 use_asynchronous_shaders{device.UseAsynchronousShaders()}, 176 use_asynchronous_shaders{device.UseAsynchronousShaders()},
177 strict_context_required{device.StrictContextRequired()},
177 profile{ 178 profile{
178 .supported_spirv = 0x00010000, 179 .supported_spirv = 0x00010000,
179 180
@@ -255,9 +256,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
255 } 256 }
256 shader_cache_filename = base_dir / "opengl.bin"; 257 shader_cache_filename = base_dir / "opengl.bin";
257 258
258 if (!workers) { 259 if (!workers && !strict_context_required) {
259 workers = CreateWorkers(); 260 workers = CreateWorkers();
260 } 261 }
262 std::optional<Context> strict_context;
263 if (strict_context_required) {
264 strict_context.emplace(emu_window);
265 }
266
261 struct { 267 struct {
262 std::mutex mutex; 268 std::mutex mutex;
263 size_t total{}; 269 size_t total{};
@@ -265,44 +271,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
265 bool has_loaded{}; 271 bool has_loaded{};
266 } state; 272 } state;
267 273
274 const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) {
275 if (strict_context_required) {
276 work(&strict_context.value());
277 } else {
278 workers->QueueWork(std::move(work));
279 }
280 }};
268 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 281 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
269 ComputePipelineKey key; 282 ComputePipelineKey key;
270 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 283 file.read(reinterpret_cast<char*>(&key), sizeof(key));
271 workers->QueueWork( 284 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
272 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 285 ctx->pools.ReleaseContents();
273 ctx->pools.ReleaseContents(); 286 auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
274 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 287 std::scoped_lock lock{state.mutex};
275 std::scoped_lock lock{state.mutex}; 288 if (pipeline) {
276 if (pipeline) { 289 compute_cache.emplace(key, std::move(pipeline));
277 compute_cache.emplace(key, std::move(pipeline)); 290 }
278 } 291 ++state.built;
279 ++state.built; 292 if (state.has_loaded) {
280 if (state.has_loaded) { 293 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
281 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 294 }
282 } 295 });
283 });
284 ++state.total; 296 ++state.total;
285 }}; 297 }};
286 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { 298 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
287 GraphicsPipelineKey key; 299 GraphicsPipelineKey key;
288 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 300 file.read(reinterpret_cast<char*>(&key), sizeof(key));
289 workers->QueueWork( 301 queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable {
290 [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { 302 boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
291 boost::container::static_vector<Shader::Environment*, 5> env_ptrs; 303 for (auto& env : envs) {
292 for (auto& env : envs) { 304 env_ptrs.push_back(&env);
293 env_ptrs.push_back(&env); 305 }
294 } 306 ctx->pools.ReleaseContents();
295 ctx->pools.ReleaseContents(); 307 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
296 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 308 std::scoped_lock lock{state.mutex};
297 std::scoped_lock lock{state.mutex}; 309 if (pipeline) {
298 if (pipeline) { 310 graphics_cache.emplace(key, std::move(pipeline));
299 graphics_cache.emplace(key, std::move(pipeline)); 311 }
300 } 312 ++state.built;
301 ++state.built; 313 if (state.has_loaded) {
302 if (state.has_loaded) { 314 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
303 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 315 }
304 } 316 });
305 });
306 ++state.total; 317 ++state.total;
307 }}; 318 }};
308 LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); 319 LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics);
@@ -314,6 +325,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
314 state.has_loaded = true; 325 state.has_loaded = true;
315 lock.unlock(); 326 lock.unlock();
316 327
328 if (strict_context_required) {
329 return;
330 }
317 workers->WaitForRequests(stop_loading); 331 workers->WaitForRequests(stop_loading);
318 if (!use_asynchronous_shaders) { 332 if (!use_asynchronous_shaders) {
319 workers.reset(); 333 workers.reset();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 53ffea904..f82420592 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -69,6 +69,7 @@ private:
69 StateTracker& state_tracker; 69 StateTracker& state_tracker;
70 VideoCore::ShaderNotify& shader_notify; 70 VideoCore::ShaderNotify& shader_notify;
71 const bool use_asynchronous_shaders; 71 const bool use_asynchronous_shaders;
72 const bool strict_context_required;
72 73
73 GraphicsPipelineKey graphics_key{}; 74 GraphicsPipelineKey graphics_key{};
74 GraphicsPipeline* current_pipeline{}; 75 GraphicsPipeline* current_pipeline{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 5b5e178ad..bc75680f0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -140,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
140 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 140 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
141 std::unique_ptr<Core::Frontend::GraphicsContext> context_) 141 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
142 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, 142 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
143 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, 143 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_},
144 program_manager{device}, 144 state_tracker{}, program_manager{device},
145 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { 145 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) {
146 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { 146 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
147 glEnable(GL_DEBUG_OUTPUT); 147 glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 18be54729..f502a7d09 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
139 RenderScreenshot(*framebuffer, use_accelerated); 139 RenderScreenshot(*framebuffer, use_accelerated);
140 140
141 bool has_been_recreated = false; 141 bool has_been_recreated = false;
142 const auto recreate_swapchain = [&] { 142 const auto recreate_swapchain = [&](u32 width, u32 height) {
143 if (!has_been_recreated) { 143 if (!has_been_recreated) {
144 has_been_recreated = true; 144 has_been_recreated = true;
145 scheduler.Finish(); 145 scheduler.Finish();
146 } 146 }
147 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 147 swapchain.Create(width, height, is_srgb);
148 swapchain.Create(layout.width, layout.height, is_srgb);
149 }; 148 };
150 if (swapchain.NeedsRecreation(is_srgb)) { 149
151 recreate_swapchain(); 150 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
151 if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
152 swapchain.GetHeight() != layout.height) {
153 recreate_swapchain(layout.width, layout.height);
152 } 154 }
153 bool is_outdated; 155 bool is_outdated;
154 do { 156 do {
155 swapchain.AcquireNextImage(); 157 swapchain.AcquireNextImage();
156 is_outdated = swapchain.IsOutDated(); 158 is_outdated = swapchain.IsOutDated();
157 if (is_outdated) { 159 if (is_outdated) {
158 recreate_swapchain(); 160 recreate_swapchain(layout.width, layout.height);
159 } 161 }
160 } while (is_outdated); 162 } while (is_outdated);
161 if (has_been_recreated) { 163 if (has_been_recreated) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 558b8db56..84d36fea6 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -285,6 +285,9 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) {
285 285
286void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, 286void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size,
287 u32 stride) { 287 u32 stride) {
288 if (index >= device.GetMaxVertexInputBindings()) {
289 return;
290 }
288 if (device.IsExtExtendedDynamicStateSupported()) { 291 if (device.IsExtExtendedDynamicStateSupported()) {
289 scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { 292 scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
290 const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; 293 const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 006128638..4b10fe7bc 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -529,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
530 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; 530 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
531 if (key.state.dynamic_vertex_input) { 531 if (key.state.dynamic_vertex_input) {
532 for (size_t index = 0; index < key.state.attributes.size(); ++index) { 532 const size_t num_vertex_arrays = std::min(
533 key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings()));
534 for (size_t index = 0; index < num_vertex_arrays; ++index) {
533 const u32 type = key.state.DynamicAttributeType(index); 535 const u32 type = key.state.DynamicAttributeType(index);
534 if (!stage_infos[0].loads.Generic(index) || type == 0) { 536 if (!stage_infos[0].loads.Generic(index) || type == 0) {
535 continue; 537 continue;
@@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
551 }); 553 });
552 } 554 }
553 } else { 555 } else {
554 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 556 const size_t num_vertex_arrays = std::min(
557 Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
558 for (size_t index = 0; index < num_vertex_arrays; ++index) {
555 const bool instanced = key.state.binding_divisors[index] != 0; 559 const bool instanced = key.state.binding_divisors[index] != 0;
556 const auto rate = 560 const auto rate =
557 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 561 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
@@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
580 }); 584 });
581 } 585 }
582 } 586 }
587 ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
588
583 VkPipelineVertexInputStateCreateInfo vertex_input_ci{ 589 VkPipelineVertexInputStateCreateInfo vertex_input_ci{
584 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 590 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
585 .pNext = nullptr, 591 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 81f5f3e11..86fdde014 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -341,6 +341,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
341 .support_snorm_render_buffer = true, 341 .support_snorm_render_buffer = true,
342 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 342 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
343 }; 343 };
344
345 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
346 LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}",
347 device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes);
348 }
349 if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) {
350 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
351 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
352 }
344} 353}
345 354
346PipelineCache::~PipelineCache() = default; 355PipelineCache::~PipelineCache() = default;
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index d7be417f5..b6810eef9 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -67,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
67 67
68} // Anonymous namespace 68} // Anonymous namespace
69 69
70Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, 70Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
71 u32 height, bool srgb) 71 u32 width_, u32 height_, bool srgb)
72 : surface{surface_}, device{device_}, scheduler{scheduler_} { 72 : surface{surface_}, device{device_}, scheduler{scheduler_} {
73 Create(width, height, srgb); 73 Create(width_, height_, srgb);
74} 74}
75 75
76Swapchain::~Swapchain() = default; 76Swapchain::~Swapchain() = default;
77 77
78void Swapchain::Create(u32 width, u32 height, bool srgb) { 78void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
79 is_outdated = false; 79 is_outdated = false;
80 is_suboptimal = false; 80 is_suboptimal = false;
81 width = width_;
82 height = height_;
81 83
82 const auto physical_device = device.GetPhysical(); 84 const auto physical_device = device.GetPhysical();
83 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; 85 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
@@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) {
88 device.GetLogical().WaitIdle(); 90 device.GetLogical().WaitIdle();
89 Destroy(); 91 Destroy();
90 92
91 CreateSwapchain(capabilities, width, height, srgb); 93 CreateSwapchain(capabilities, srgb);
92 CreateSemaphores(); 94 CreateSemaphores();
93 CreateImageViews(); 95 CreateImageViews();
94 96
@@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
148 } 150 }
149} 151}
150 152
151void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, 153void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
152 bool srgb) {
153 const auto physical_device{device.GetPhysical()}; 154 const auto physical_device{device.GetPhysical()};
154 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; 155 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
155 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 156 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 111b3902d..caf1ff32b 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -80,9 +80,16 @@ public:
80 return *present_semaphores[frame_index]; 80 return *present_semaphores[frame_index];
81 } 81 }
82 82
83 u32 GetWidth() const {
84 return width;
85 }
86
87 u32 GetHeight() const {
88 return height;
89 }
90
83private: 91private:
84 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, 92 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
85 bool srgb);
86 void CreateSemaphores(); 93 void CreateSemaphores();
87 void CreateImageViews(); 94 void CreateImageViews();
88 95
@@ -105,6 +112,9 @@ private:
105 std::vector<u64> resource_ticks; 112 std::vector<u64> resource_ticks;
106 std::vector<vk::Semaphore> present_semaphores; 113 std::vector<vk::Semaphore> present_semaphores;
107 114
115 u32 width;
116 u32 height;
117
108 u32 image_index{}; 118 u32 image_index{};
109 u32 frame_index{}; 119 u32 frame_index{};
110 120
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 6a2ad4b1d..67540cb80 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -1380,6 +1380,10 @@ void Device::SetupFeatures() {
1380 is_shader_storage_image_multisample = features.shaderStorageImageMultisample; 1380 is_shader_storage_image_multisample = features.shaderStorageImageMultisample;
1381 is_blit_depth_stencil_supported = TestDepthStencilBlits(); 1381 is_blit_depth_stencil_supported = TestDepthStencilBlits();
1382 is_optimal_astc_supported = IsOptimalAstcSupported(features); 1382 is_optimal_astc_supported = IsOptimalAstcSupported(features);
1383
1384 const VkPhysicalDeviceLimits& limits{properties.limits};
1385 max_vertex_input_attributes = limits.maxVertexInputAttributes;
1386 max_vertex_input_bindings = limits.maxVertexInputBindings;
1383} 1387}
1384 1388
1385void Device::SetupProperties() { 1389void Device::SetupProperties() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index db802437c..391b7604c 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -368,6 +368,14 @@ public:
368 return must_emulate_bgr565; 368 return must_emulate_bgr565;
369 } 369 }
370 370
371 u32 GetMaxVertexInputAttributes() const {
372 return max_vertex_input_attributes;
373 }
374
375 u32 GetMaxVertexInputBindings() const {
376 return max_vertex_input_bindings;
377 }
378
371private: 379private:
372 /// Checks if the physical device is suitable. 380 /// Checks if the physical device is suitable.
373 void CheckSuitability(bool requires_swapchain) const; 381 void CheckSuitability(bool requires_swapchain) const;
@@ -467,6 +475,8 @@ private:
467 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 475 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
468 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 476 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
469 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 477 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
478 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
479 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
470 480
471 // Telemetry parameters 481 // Telemetry parameters
472 std::string vendor_name; ///< Device's driver name. 482 std::string vendor_name; ///< Device's driver name.
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5b5b6fed8..1a47fb9c9 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -61,8 +61,6 @@ void EmuThread::run() {
61 61
62 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU 62 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
63 // execution. 63 // execution.
64 gpu.Start();
65
66 gpu.ObtainContext(); 64 gpu.ObtainContext();
67 65
68 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 66 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@@ -77,6 +75,7 @@ void EmuThread::run() {
77 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 75 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
78 76
79 gpu.ReleaseContext(); 77 gpu.ReleaseContext();
78 gpu.Start();
80 79
81 system.GetCpuManager().OnGpuReady(); 80 system.GetCpuManager().OnGpuReady();
82 81
@@ -224,6 +223,7 @@ class RenderWidget : public QWidget {
224public: 223public:
225 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { 224 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
226 setAttribute(Qt::WA_NativeWindow); 225 setAttribute(Qt::WA_NativeWindow);
226 setAttribute(Qt::WA_DontCreateNativeAncestors);
227 setAttribute(Qt::WA_PaintOnScreen); 227 setAttribute(Qt::WA_PaintOnScreen);
228 } 228 }
229 229
@@ -314,6 +314,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
314 input_subsystem->Initialize(); 314 input_subsystem->Initialize();
315 this->setMouseTracking(true); 315 this->setMouseTracking(true);
316 316
317 strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland");
318
317 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 319 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
318 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, 320 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
319 Qt::QueuedConnection); 321 Qt::QueuedConnection);
@@ -952,6 +954,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
952 954
953bool GRenderWindow::InitializeOpenGL() { 955bool GRenderWindow::InitializeOpenGL() {
954#ifdef HAS_OPENGL 956#ifdef HAS_OPENGL
957 if (!QOpenGLContext::supportsThreadedOpenGL()) {
958 QMessageBox::warning(this, tr("OpenGL not available!"),
959 tr("OpenGL shared contexts are not supported."));
960 return false;
961 }
962
955 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 963 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
956 // WA_DontShowOnScreen, WA_DeleteOnClose 964 // WA_DontShowOnScreen, WA_DeleteOnClose
957 auto child = new OpenGLRenderWidget(this); 965 auto child = new OpenGLRenderWidget(this);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 6c204416f..885e24990 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2915,9 +2915,14 @@ static QScreen* GuessCurrentScreen(QWidget* window) {
2915 }); 2915 });
2916} 2916}
2917 2917
2918bool GMainWindow::UsingExclusiveFullscreen() {
2919 return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive ||
2920 QGuiApplication::platformName() == QStringLiteral("wayland");
2921}
2922
2918void GMainWindow::ShowFullscreen() { 2923void GMainWindow::ShowFullscreen() {
2919 const auto show_fullscreen = [](QWidget* window) { 2924 const auto show_fullscreen = [this](QWidget* window) {
2920 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2925 if (UsingExclusiveFullscreen()) {
2921 window->showFullScreen(); 2926 window->showFullScreen();
2922 return; 2927 return;
2923 } 2928 }
@@ -2945,7 +2950,7 @@ void GMainWindow::ShowFullscreen() {
2945 2950
2946void GMainWindow::HideFullscreen() { 2951void GMainWindow::HideFullscreen() {
2947 if (ui->action_Single_Window_Mode->isChecked()) { 2952 if (ui->action_Single_Window_Mode->isChecked()) {
2948 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2953 if (UsingExclusiveFullscreen()) {
2949 showNormal(); 2954 showNormal();
2950 restoreGeometry(UISettings::values.geometry); 2955 restoreGeometry(UISettings::values.geometry);
2951 } else { 2956 } else {
@@ -2959,7 +2964,7 @@ void GMainWindow::HideFullscreen() {
2959 statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); 2964 statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
2960 ui->menubar->show(); 2965 ui->menubar->show();
2961 } else { 2966 } else {
2962 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2967 if (UsingExclusiveFullscreen()) {
2963 render_window->showNormal(); 2968 render_window->showNormal();
2964 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 2969 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
2965 } else { 2970 } else {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 62d629973..27644fae5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -320,6 +320,7 @@ private slots:
320 void OnDisplayTitleBars(bool); 320 void OnDisplayTitleBars(bool);
321 void InitializeHotkeys(); 321 void InitializeHotkeys();
322 void ToggleFullscreen(); 322 void ToggleFullscreen();
323 bool UsingExclusiveFullscreen();
323 void ShowFullscreen(); 324 void ShowFullscreen();
324 void HideFullscreen(); 325 void HideFullscreen();
325 void ToggleWindowMode(); 326 void ToggleWindowMode();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 37dd1747c..31f28a507 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -115,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const {
115 115
116void EmuWindow_SDL2::OnResize() { 116void EmuWindow_SDL2::OnResize() {
117 int width, height; 117 int width, height;
118 SDL_GetWindowSize(render_window, &width, &height); 118 SDL_GL_GetDrawableSize(render_window, &width, &height);
119 UpdateCurrentFramebufferLayout(width, height); 119 UpdateCurrentFramebufferLayout(width, height);
120} 120}
121 121
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 9b660c13c..ddcb048d6 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
104 exit(1); 104 exit(1);
105 } 105 }
106 106
107 strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
108
107 SetWindowIcon(); 109 SetWindowIcon();
108 110
109 if (fullscreen) { 111 if (fullscreen) {