diff options
Diffstat (limited to 'src/video_core')
34 files changed, 253 insertions, 109 deletions
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index f9a6472cf..92d77eef2 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -535,7 +535,7 @@ private: const u64* const state_words = Array<type>(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; - const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords()); const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 5d3a8293b..6881b34c4 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -19,6 +19,7 @@ #include "common/literals.h" #include "common/lru_cache.h" #include "common/microprofile.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/memory.h" #include "video_core/buffer_cache/buffer_base.h" diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 584a0c26c..cdaf4f8d5 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -35,8 +35,6 @@ public: explicit ChannelInfo(Tegra::Control::ChannelState& state); ChannelInfo(const ChannelInfo& state) = delete; ChannelInfo& operator=(const ChannelInfo&) = delete; - ChannelInfo(ChannelInfo&& other) = default; - ChannelInfo& operator=(ChannelInfo&& other) = default; Tegra::Engines::Maxwell3D& maxwell3d; Tegra::Engines::KeplerCompute& kepler_compute; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 55462752c..34bbc72cf 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -126,7 +126,6 @@ void Maxwell3D::InitializeRegisterDefaults() { draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true; draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true; draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true; - draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true; } void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { @@ -218,16 +217,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume regs.index_buffer.count = regs.index_buffer32_first.count; regs.index_buffer.first = regs.index_buffer32_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(index_buffer16_first): regs.index_buffer.count = regs.index_buffer16_first.count; regs.index_buffer.first = regs.index_buffer16_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(index_buffer8_first): regs.index_buffer.count = regs.index_buffer8_first.count; regs.index_buffer.first = regs.index_buffer8_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; @@ -300,21 +302,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { draw_mode = DrawMode::InlineIndex; }; switch (method) { + case MAXWELL3D_REG_INDEX(draw.begin): { + draw_mode = + (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || + (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged) + ? DrawMode::Instance + : DrawMode::General; + break; + } case MAXWELL3D_REG_INDEX(draw.end): switch (draw_mode) { case DrawMode::General: - ProcessDraw(1); + ProcessDraw(); break; case DrawMode::InlineIndex: regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4); regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; - ProcessDraw(1); + draw_indexed = true; + ProcessDraw(); inline_index_draw_indexes.clear(); break; case DrawMode::Instance: break; } break; + case MAXWELL3D_REG_INDEX(index_buffer.count): + draw_indexed = true; + break; case MAXWELL3D_REG_INDEX(draw_inline_index): update_inline_index(method_argument); break; @@ -328,13 +342,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { update_inline_index(regs.inline_index_4x8.index2); update_inline_index(regs.inline_index_4x8.index3); break; - case MAXWELL3D_REG_INDEX(draw.instance_id): - draw_mode = - (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || - (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged) - ? DrawMode::Instance - : DrawMode::General; - break; } } else { ProcessDeferredDraw(); @@ -624,27 +631,16 @@ void Maxwell3D::ProcessClearBuffers(u32 layer_count) { void Maxwell3D::ProcessDraw(u32 instance_count) { LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), - regs.vertex_buffer.count); - - ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?"); - - // Both instance configuration registers can not be set at the same time. - ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First || - regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged, - "Illegal combination of instancing parameters"); + draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count); ProcessTopologyOverride(); - const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count; if (ShouldExecute()) { - rasterizer->Draw(is_indexed, instance_count); + rasterizer->Draw(draw_indexed, instance_count); } - if (is_indexed) { - regs.index_buffer.count = 0; - } else { - regs.vertex_buffer.count = 0; - } + draw_indexed = false; + deferred_draw_method.clear(); } void Maxwell3D::ProcessDeferredDraw() { @@ -667,8 +663,6 @@ void Maxwell3D::ProcessDeferredDraw() { ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?"); ProcessDraw(instance_count); - - deferred_draw_method.clear(); } } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index deba292a5..a541cd95f 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -3159,6 +3159,7 @@ private: std::vector<u32> deferred_draw_method; enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; DrawMode draw_mode{DrawMode::General}; + bool draw_indexed{}; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 1bd477011..164a5252a 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -125,7 +125,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); if (block) { - state.cv.wait(lk, thread.get_stop_token(), [this, fence] { + Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] { return fence <= state.signaled_fence.load(std::memory_order_relaxed); }); } diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 64628d3e3..c71a419c7 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,6 +10,7 @@ #include <thread> #include <variant> +#include "common/polyfill_thread.h" #include "common/threadsafe_queue.h" #include "video_core/framebuffer_config.h" diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index cfd872a40..b6907463c 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -6,8 +6,8 @@ #include <functional> #include <optional> #include <span> -#include <stop_token> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1663e277d..e2e3dac34 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -14,6 +14,7 @@ #include "common/literals.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "shader_recompiler/stage.h" #include "video_core/renderer_opengl/gl_device.h" diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 3fe04a115..a38060100 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -39,6 +39,7 @@ using Shader::Backend::GLASM::EmitGLASM; using Shader::Backend::GLSL::EmitGLSL; using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -56,6 +57,17 @@ auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -220,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .support_int64 = device.HasShaderInt64(), .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, + .support_viewport_index_layer = device.HasVertexViewportLayer(), } { if (use_asynchronous_shaders) { workers = CreateWorkers(); @@ -314,9 +327,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { const auto& regs{maxwell3d->regs}; graphics_key.raw = 0; graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); - graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 - ? regs.draw.topology.Value() - : Maxwell::PrimitiveTopology{}); + graphics_key.gs_input_topology.Assign(regs.draw.topology.Value()); graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); graphics_key.tessellation_clockwise.Assign( @@ -415,7 +426,19 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without GL_ARB_shader_viewport_layer_array + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.gs_input_topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -443,6 +466,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()}; const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit}; @@ -456,7 +483,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( const bool use_glasm{device.UseAssemblyShaders()}; const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0; for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 89f181fe3..53ffea904 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -4,7 +4,6 @@ #pragma once #include <filesystem> -#include <stop_token> #include <unordered_map> #include "common/common_types.h" diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 98cc26679..f3f08b42c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -7,6 +7,7 @@ #include "common/bit_cast.h" #include "common/cityhash.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 89426121f..6e5abade4 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/math_util.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "core/frontend/emu_window.h" diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index c7196b64e..b5ae6443c 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" #include "video_core/renderer_vulkan/vk_scheduler.h" diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 362ed579a..689f02ea5 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -7,6 +7,7 @@ #include <thread> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d4b0a542a..29da442fa 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache); namespace { using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -53,13 +54,24 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 7; +constexpr u32 CACHE_VERSION = 8; template <typename Container> auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { switch (comparison) { case Maxwell::ComparisonOp::Never_D3D: @@ -277,7 +289,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device const auto& float_control{device.FloatControlProperties()}; const VkDriverIdKHR driver_id{device.GetDriverID()}; profile = Shader::Profile{ - .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, + .supported_spirv = device.SupportedSpirvVersion(), .unified_descriptor_binding = true, .support_descriptor_aliasing = true, .support_int8 = device.IsInt8Supported(), @@ -327,6 +339,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, .support_snorm_render_buffer = true, + .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), }; } @@ -509,7 +522,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without VK_EXT_shader_viewport_index_layer + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.state.topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -530,6 +555,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules; @@ -538,7 +567,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( Shader::Backend::Bindings binding; for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index dc21b7e69..91ad4bf57 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -12,7 +12,7 @@ namespace Vulkan { struct RenderPassKey { - auto operator<=>(const RenderPassKey&) const noexcept = default; + bool operator==(const RenderPassKey&) const noexcept = default; std::array<VideoCore::Surface::PixelFormat, 8> color_formats; VideoCore::Surface::PixelFormat depth_format; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 4a7b633b7..c09fb3e98 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -145,7 +145,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { if (work_queue.empty()) { wait_cv.notify_all(); } - work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); + Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); }); if (stop_token.stop_requested()) { continue; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 929216749..3858c506c 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -12,6 +12,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 706d9ba74..d7be417f5 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "video_core/renderer_vulkan/vk_scheduler.h" diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index a4391202d..f3cc4c70b 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -12,6 +12,7 @@ #include <vector> #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/control/channel_state_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/shader_environment.h" diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index f24f320b6..958810747 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -15,6 +15,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" #include "video_core/memory_manager.h" diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index bb55b029f..1342fab1e 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -10,12 +10,12 @@ #include <memory> #include <optional> #include <span> -#include <stop_token> #include <type_traits> #include <unordered_map> #include <vector> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "common/unique_function.h" #include "shader_recompiler/environment.h" #include "video_core/engines/maxwell_3d.h" diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp index ee4f2d406..418890126 100644 --- a/src/video_core/texture_cache/formatter.cpp +++ b/src/video_core/texture_cache/formatter.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <string> +#include "common/polyfill_ranges.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/image_base.h" #include "video_core/texture_cache/image_info.h" diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h index 1efbd6507..0829d773a 100644 --- a/src/video_core/texture_cache/render_targets.h +++ b/src/video_core/texture_cache/render_targets.h @@ -13,7 +13,7 @@ namespace VideoCommon { /// Framebuffer properties used to lookup a framebuffer struct RenderTargets { - constexpr auto operator<=>(const RenderTargets&) const noexcept = default; + constexpr bool operator==(const RenderTargets&) const noexcept = default; constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept { const auto contains = [elements](ImageViewId item) { diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 46e8a86e6..1e2aad76a 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" namespace VideoCommon { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 9db7195bf..587339a31 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -16,6 +16,7 @@ #include "common/hash.h" #include "common/literals.h" #include "common/lru_cache.h" +#include "common/polyfill_ranges.h" #include "video_core/compatible_formats.h" #include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" @@ -60,8 +61,6 @@ public: TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; - TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default; - TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default; DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 69a32819a..e8d7c7863 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -15,6 +15,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "common/thread_worker.h" #include "video_core/textures/astc.h" diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 45071185a..155599316 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/shader_info.h" #include "video_core/transform_feedback.h" diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ddecfca13..652329c38 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/literals.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "video_core/vulkan_common/nsight_aftermath_tracker.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -74,23 +75,14 @@ enum class NvidiaArchitecture { }; constexpr std::array REQUIRED_EXTENSIONS{ - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, - VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, - VK_KHR_16BIT_STORAGE_EXTENSION_NAME, - VK_KHR_8BIT_STORAGE_EXTENSION_NAME, - VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, - VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, - VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, - VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, - VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, - VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, + + // Core in 1.2, but required due to use of extension methods, + // and well-supported by drivers + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, + VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, - VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, #ifdef _WIN32 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, #endif @@ -99,6 +91,17 @@ constexpr std::array REQUIRED_EXTENSIONS{ #endif }; +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{ + VK_KHR_8BIT_STORAGE_EXTENSION_NAME, + VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, + VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, + VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, +}; + +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{ + VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, +}; + template <typename T> void SetNext(void**& next, T& data) { *next = &data; @@ -327,7 +330,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, - supported_extensions{GetSupportedExtensions(physical)}, + instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions( + physical)}, format_properties(GetFormatProperties(physical)) { CheckSuitability(surface != nullptr); SetupFamilies(surface); @@ -451,8 +455,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, variable_pointers); - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, .pNext = nullptr, .shaderDemoteToHelperInvocation = true, }; @@ -896,28 +900,51 @@ std::string Device::GetDriverName() const { } } +static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { + std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; + + if (available_version < VK_API_VERSION_1_2) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_2.end()); + } + + if (available_version < VK_API_VERSION_1_3) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_3.end()); + } + + return extensions; +} + void Device::CheckSuitability(bool requires_swapchain) const { - std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; - bool has_swapchain = false; - for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { - const std::string_view name{property.extensionName}; - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; - } - available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; - } - has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME; + std::vector<const char*> required_extensions = + ExtensionsRequiredForInstanceVersion(instance_version); + std::vector<const char*> available_extensions; + + if (requires_swapchain) { + required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; + + auto extension_properties = physical.EnumerateDeviceExtensionProperties(); + + for (const VkExtensionProperties& property : extension_properties) { + available_extensions.push_back(property.extensionName); + } + + bool has_all_required_extensions = true; + for (const char* requirement_name : required_extensions) { + const bool found = + std::ranges::any_of(available_extensions, [&](const char* extension_name) { + return std::strcmp(requirement_name, extension_name) == 0; + }); + + if (!found) { + LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name); + has_all_required_extensions = false; } - LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]); - throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } - if (requires_swapchain && !has_swapchain) { - LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); + + if (!has_all_required_extensions) { throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } @@ -940,9 +967,8 @@ void Device::CheckSuitability(bool requires_swapchain) const { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; - demote.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT; + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{}; + demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES; demote.pNext = nullptr; VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; @@ -960,7 +986,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { physical.GetFeatures2KHR(features2); const VkPhysicalDeviceFeatures& features{features2.features}; - const std::array feature_report{ + std::vector feature_report{ std::make_pair(features.robustBufferAccess, "robustBufferAccess"), std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), std::make_pair(features.imageCubeArray, "imageCubeArray"), @@ -983,27 +1009,30 @@ void Device::CheckSuitability(bool requires_swapchain) const { "shaderStorageImageWriteWithoutFormat"), std::make_pair(features.shaderClipDistance, "shaderClipDistance"), std::make_pair(features.shaderCullDistance, "shaderCullDistance"), - std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), std::make_pair(variable_pointers.variablePointers, "variablePointers"), std::make_pair(variable_pointers.variablePointersStorageBuffer, "variablePointersStorageBuffer"), std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), + std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), }; + + bool has_all_required_features = true; for (const auto& [is_supported, name] : feature_report) { - if (is_supported) { - continue; + if (!is_supported) { + LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + has_all_required_features = false; } - LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + } + + if (!has_all_required_features) { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } std::vector<const char*> Device::LoadExtensions(bool requires_surface) { - std::vector<const char*> extensions; - extensions.reserve(8 + REQUIRED_EXTENSIONS.size()); - extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); + std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version); if (requires_surface) { extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d7cc6c593..c85fbba77 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -211,11 +211,6 @@ public: return khr_uniform_buffer_standard_layout; } - /// Returns true if the device supports VK_KHR_spirv_1_4. - bool IsKhrSpirv1_4Supported() const { - return khr_spirv_1_4; - } - /// Returns true if the device supports VK_KHR_push_descriptor. bool IsKhrPushDescriptorSupported() const { return khr_push_descriptor; @@ -316,6 +311,17 @@ public: return ext_shader_atomic_int64; } + /// Returns the minimum supported version of SPIR-V. + u32 SupportedSpirvVersion() const { + if (instance_version >= VK_API_VERSION_1_3) { + return 0x00010600U; + } + if (khr_spirv_1_4) { + return 0x00010400U; + } + return 0x00010000U; + } + /// Returns true when a known debugging tool is attached. bool HasDebuggingToolAttached() const { return has_renderdoc || has_nsight_graphics; diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index a082e3059..562039b56 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -9,18 +9,21 @@ #include "common/common_types.h" #include "common/dynamic_library.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "core/frontend/emu_window.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_wrapper.h" // Include these late to avoid polluting previous headers -#ifdef _WIN32 +#if defined(_WIN32) #include <windows.h> // ensure include order #include <vulkan/vulkan_win32.h> -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include <vulkan/vulkan_macos.h> +#elif defined(__ANDROID__) +#include <vulkan/vulkan_android.h> +#else #include <X11/Xlib.h> #include <vulkan/vulkan_wayland.h> #include <vulkan/vulkan_xlib.h> @@ -39,8 +42,15 @@ namespace { case Core::Frontend::WindowSystemType::Windows: extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); break; -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + case Core::Frontend::WindowSystemType::Cocoa: + extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); + break; +#elif defined(__ANDROID__) + case Core::Frontend::WindowSystemType::Android: + extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + break; +#else case Core::Frontend::WindowSystemType::X11: extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); break; @@ -59,6 +69,10 @@ namespace { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + +#ifdef __APPLE__ + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); +#endif return extensions; } @@ -140,7 +154,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD } vk::Instance instance = std::async([&] { - return vk::Instance::Create(required_version, layers, extensions, dld); + return vk::Instance::Create(available_version, layers, extensions, dld); }).get(); if (!vk::Load(*instance, dld)) { LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 6442898bd..1732866e0 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #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" diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp index 69f9c494b..fa9bafa20 100644 --- a/src/video_core/vulkan_common/vulkan_surface.cpp +++ b/src/video_core/vulkan_common/vulkan_surface.cpp @@ -11,9 +11,11 @@ #include <windows.h> // ensure include order #include <vulkan/vulkan_win32.h> -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include <vulkan/vulkan_macos.h> +#elif defined(__ANDROID__) +#include <vulkan/vulkan_android.h> +#else #include <X11/Xlib.h> #include <vulkan/vulkan_wayland.h> #include <vulkan/vulkan_xlib.h> @@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } } -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { + const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, + nullptr, 0, window_info.render_surface}; + const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK")); + if (!vkCreateMacOSSurfaceMVK || + vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#elif defined(__ANDROID__) + if (window_info.type == Core::Frontend::WindowSystemType::Android) { + const VkAndroidSurfaceCreateInfoKHR android_ci{ + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0, + reinterpret_cast<ANativeWindow*>(window_info.render_surface)}; + const auto vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR")); + if (!vkCreateAndroidSurfaceKHR || + vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) != + VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#else if (window_info.type == Core::Frontend::WindowSystemType::X11) { const VkXlibSurfaceCreateInfoKHR xlib_ci{ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, @@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, } } #endif + if (!unsafe_surface) { LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); |