diff options
Diffstat (limited to 'src/video_core')
105 files changed, 5450 insertions, 2149 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index fd71bf186..4742bcbe9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(video_core STATIC buffer_cache/buffer_base.h buffer_cache/buffer_cache.cpp buffer_cache/buffer_cache.h + cache_types.h cdma_pusher.cpp cdma_pusher.h compatible_formats.cpp @@ -51,6 +52,8 @@ add_library(video_core STATIC engines/puller.cpp engines/puller.h framebuffer_config.h + fsr.cpp + fsr.h host1x/codecs/codec.cpp host1x/codecs/codec.h host1x/codecs/h264.cpp @@ -84,6 +87,7 @@ add_library(video_core STATIC gpu.h gpu_thread.cpp gpu_thread.h + invalidation_accumulator.h memory_manager.cpp memory_manager.h precompiled_headers.h @@ -98,6 +102,8 @@ add_library(video_core STATIC renderer_null/null_rasterizer.h renderer_null/renderer_null.cpp renderer_null/renderer_null.h + renderer_opengl/blit_image.cpp + renderer_opengl/blit_image.h renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h renderer_opengl/gl_compute_pipeline.cpp @@ -106,6 +112,8 @@ add_library(video_core STATIC renderer_opengl/gl_device.h renderer_opengl/gl_fence_manager.cpp renderer_opengl/gl_fence_manager.h + renderer_opengl/gl_fsr.cpp + renderer_opengl/gl_fsr.h renderer_opengl/gl_graphics_pipeline.cpp renderer_opengl/gl_graphics_pipeline.h renderer_opengl/gl_rasterizer.cpp @@ -189,6 +197,8 @@ add_library(video_core STATIC renderer_vulkan/vk_texture_cache.cpp renderer_vulkan/vk_texture_cache.h renderer_vulkan/vk_texture_cache_base.cpp + renderer_vulkan/vk_turbo_mode.cpp + renderer_vulkan/vk_turbo_mode.h renderer_vulkan/vk_update_descriptor.cpp renderer_vulkan/vk_update_descriptor.h shader_cache.cpp diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index 92d77eef2..c47b7d866 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -430,7 +430,7 @@ private: if (query_begin >= SizeBytes() || size < 0) { return; } - u64* const untracked_words = Array<Type::Untracked>(); + [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>(); u64* const state_words = Array<type>(); const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; @@ -483,7 +483,7 @@ private: NotifyRasterizer<true>(word_index, current_bits, ~u64{0}); } // Exclude CPU modified pages when visiting GPU pages - const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0); + const u64 word = current_word; u64 page = page_begin; page_begin = 0; @@ -531,7 +531,7 @@ private: [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { static_assert(type != Type::Untracked); - const u64* const untracked_words = Array<Type::Untracked>(); + [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); 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; @@ -539,8 +539,7 @@ private: 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) { - const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; - const u64 word = state_words[word_index] & ~off_word; + const u64 word = state_words[word_index]; if (word == 0) { continue; } @@ -564,7 +563,7 @@ private: [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { static_assert(type != Type::Untracked); - const u64* const untracked_words = Array<Type::Untracked>(); + [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); 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; @@ -574,8 +573,7 @@ private: u64 begin = std::numeric_limits<u64>::max(); u64 end = 0; for (u64 word_index = word_begin; word_index < word_end; ++word_index) { - const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; - const u64 word = state_words[word_index] & ~off_word; + const u64 word = state_words[word_index]; if (word == 0) { continue; } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 6c8d98946..627917ab6 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -200,7 +200,16 @@ public: /// Return true when a CPU region is modified from the CPU [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); - std::mutex mutex; + void SetDrawIndirect( + const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) { + current_draw_indirect = current_draw_indirect_; + } + + [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount(); + + [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer(); + + std::recursive_mutex mutex; Runtime& runtime; private: @@ -272,6 +281,8 @@ private: void BindHostVertexBuffers(); + void BindHostDrawIndirectBuffers(); + void BindHostGraphicsUniformBuffers(size_t stage); void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind); @@ -298,6 +309,8 @@ private: void UpdateVertexBuffer(u32 index); + void UpdateDrawIndirect(); + void UpdateUniformBuffers(size_t stage); void UpdateStorageBuffers(size_t stage); @@ -372,6 +385,8 @@ private: SlotVector<Buffer> slot_buffers; DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; + const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; + u32 last_index_count = 0; Binding index_buffer; @@ -380,6 +395,8 @@ private: std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers; std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers; std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers; + Binding count_buffer_binding; + Binding indirect_buffer_binding; std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers; std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers; @@ -666,13 +683,17 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) { BindHostIndexBuffer(); } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); - if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { - runtime.BindQuadArrayIndexBuffer(draw_state.vertex_buffer.first, - draw_state.vertex_buffer.count); + if (draw_state.topology == Maxwell::PrimitiveTopology::Quads || + draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { + runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first, + draw_state.vertex_buffer.count); } } BindHostVertexBuffers(); BindHostTransformFeedbackBuffers(); + if (current_draw_indirect) { + BindHostDrawIndirectBuffers(); + } } template <class P> @@ -822,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { template <class P> void BufferCache<P>::CommitAsyncFlushesHigh() { AccumulateFlushes(); + if (committed_ranges.empty()) { return; } @@ -868,7 +890,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { buffer_id, }); // Align up to avoid cache conflicts - constexpr u64 align = 256ULL; + constexpr u64 align = 8ULL; constexpr u64 mask = ~(align - 1ULL); total_size_bytes += (new_size + align - 1) & mask; largest_copy = std::max(largest_copy, new_size); @@ -1041,6 +1063,19 @@ void BufferCache<P>::BindHostVertexBuffers() { } template <class P> +void BufferCache<P>::BindHostDrawIndirectBuffers() { + const auto bind_buffer = [this](const Binding& binding) { + Buffer& buffer = slot_buffers[binding.buffer_id]; + TouchBuffer(buffer, binding.buffer_id); + SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); + }; + if (current_draw_indirect->include_count) { + bind_buffer(count_buffer_binding); + } + bind_buffer(indirect_buffer_binding); +} + +template <class P> void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) { u32 dirty = ~0U; if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { @@ -1271,6 +1306,9 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { UpdateStorageBuffers(stage); UpdateTextureBuffers(stage); } + if (current_draw_indirect) { + UpdateDrawIndirect(); + } } while (has_deleted_buffers); } @@ -1288,7 +1326,7 @@ void BufferCache<P>::UpdateIndexBuffer() { const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); const auto& index_array = draw_state.index_buffer; auto& flags = maxwell3d->dirty.flags; - if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { + if (!flags[Dirty::IndexBuffer]) { return; } flags[Dirty::IndexBuffer] = false; @@ -1361,6 +1399,27 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { } template <class P> +void BufferCache<P>::UpdateDrawIndirect() { + const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) { + const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + if (!cpu_addr) { + binding = NULL_BINDING; + return; + } + binding = Binding{ + .cpu_addr = *cpu_addr, + .size = static_cast<u32>(size), + .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), + }; + }; + if (current_draw_indirect->include_count) { + update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding); + } + update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size, + indirect_buffer_binding); +} + +template <class P> void BufferCache<P>::UpdateUniformBuffers(size_t stage) { ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) { Binding& binding = uniform_buffers[stage][index]; @@ -1879,14 +1938,21 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s bool is_written) const { const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr); const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8); - const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + const u32 alignment = runtime.GetStorageBufferAlignment(); + + const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment); + const u32 aligned_size = + Common::AlignUp(static_cast<u32>(gpu_addr - aligned_gpu_addr) + size, alignment); + + const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr); if (!cpu_addr || size == 0) { return NULL_BINDING; } - const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); + + const VAddr cpu_end = Common::AlignUp(*cpu_addr + aligned_size, Core::Memory::YUZU_PAGESIZE); const Binding binding{ .cpu_addr = *cpu_addr, - .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), + .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *cpu_addr), .buffer_id = BufferId{}, }; return binding; @@ -1940,4 +2006,16 @@ bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) } } +template <class P> +std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() { + auto& buffer = slot_buffers[count_buffer_binding.buffer_id]; + return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr)); +} + +template <class P> +std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() { + auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id]; + return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr)); +} + } // namespace VideoCommon diff --git a/src/video_core/cache_types.h b/src/video_core/cache_types.h new file mode 100644 index 000000000..1a5db3c55 --- /dev/null +++ b/src/video_core/cache_types.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace VideoCommon { + +enum class CacheType : u32 { + None = 0, + TextureCache = 1 << 0, + QueryCache = 1 << 1, + BufferCache = 1 << 2, + ShaderCache = 1 << 3, + NoTextureCache = QueryCache | BufferCache | ShaderCache, + NoBufferCache = TextureCache | QueryCache | ShaderCache, + NoQueryCache = TextureCache | BufferCache | ShaderCache, + All = TextureCache | QueryCache | BufferCache | ShaderCache, +}; +DECLARE_ENUM_FLAG_OPERATORS(CacheType) + +} // namespace VideoCommon diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 322de2606..551929824 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -61,7 +61,7 @@ bool DmaPusher::Step() { } else { const CommandListHeader command_list_header{ command_list.command_lists[dma_pushbuffer_subindex++]}; - const GPUVAddr dma_get = command_list_header.addr; + dma_state.dma_get = command_list_header.addr; if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { // We've gone through the current list, remove it from the queue @@ -75,12 +75,22 @@ bool DmaPusher::Step() { // Push buffer non-empty, read a word command_headers.resize_destructive(command_list_header.size); - if (Settings::IsGPULevelHigh()) { - memory_manager.ReadBlock(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + constexpr u32 MacroRegistersStart = 0xE00; + if (dma_state.method < MacroRegistersStart) { + if (Settings::IsGPULevelHigh()) { + memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); + } else { + memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); + } } else { - memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + const size_t copy_size = command_list_header.size * sizeof(u32); + if (subchannels[dma_state.subchannel]) { + subchannels[dma_state.subchannel]->current_dirty = + memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size); + } + memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size); } ProcessCommands(command_headers); } @@ -94,6 +104,7 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) { if (dma_state.method_count) { // Data word of methods command + dma_state.dma_word_offset = static_cast<u32>(index * sizeof(u32)); if (dma_state.non_incrementing) { const u32 max_write = static_cast<u32>( std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index); @@ -132,6 +143,8 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) { case SubmissionMode::Inline: dma_state.method = command_header.method; dma_state.subchannel = command_header.subchannel; + dma_state.dma_word_offset = static_cast<u64>( + -static_cast<s64>(dma_state.dma_get)); // negate to set address as 0 CallMethod(command_header.arg_count); dma_state.non_incrementing = true; dma_increment_once = false; @@ -164,8 +177,14 @@ void DmaPusher::CallMethod(u32 argument) const { dma_state.method_count, }); } else { - subchannels[dma_state.subchannel]->CallMethod(dma_state.method, argument, - dma_state.is_last_call); + auto subchannel = subchannels[dma_state.subchannel]; + if (!subchannel->execution_mask[dma_state.method]) [[likely]] { + subchannel->method_sink.emplace_back(dma_state.method, argument); + return; + } + subchannel->ConsumeSink(); + subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset; + subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call); } } @@ -174,8 +193,11 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count); } else { - subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, - num_methods, dma_state.method_count); + auto subchannel = subchannels[dma_state.subchannel]; + subchannel->ConsumeSink(); + subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset; + subchannel->CallMultiMethod(dma_state.method, base_start, num_methods, + dma_state.method_count); } } diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 6f00de937..1cdb690ed 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -156,6 +156,8 @@ private: u32 subchannel; ///< Current subchannel u32 method_count; ///< Current method count u32 length_pending; ///< Large NI command length pending + GPUVAddr dma_get; ///< Currently read segment + u64 dma_word_offset; ///< Current word ofset from address bool non_incrementing; ///< Current command's NI flag bool is_last_call; }; diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index 3a78421f6..1d22d25f1 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp @@ -51,6 +51,10 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) { LOG_WARNING(HW_GPU, "(STUBBED) called"); break; } + case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { + DrawTexture(); + break; + } default: break; } @@ -91,6 +95,23 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind ProcessDraw(true, num_instances); } +void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) { + draw_state.topology = topology; + + ProcessDrawIndirect(); +} + +void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, + u32 index_count) { + const auto& regs{maxwell3d->regs}; + draw_state.topology = topology; + draw_state.index_buffer = regs.index_buffer; + draw_state.index_buffer.first = index_first; + draw_state.index_buffer.count = index_count; + + ProcessDrawIndirect(); +} + void DrawManager::SetInlineIndexBuffer(u32 index) { draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff)); draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8)); @@ -162,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) { ProcessDraw(true, 1); } +void DrawManager::DrawTexture() { + const auto& regs{maxwell3d->regs}; + draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f; + draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f; + const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f; + const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f; + const bool lower_left{regs.window_origin.mode != + Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft}; + if (lower_left) { + draw_texture_state.dst_y0 -= dst_height; + } + draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width; + draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height; + draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f; + draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f; + draw_texture_state.src_x1 = + (static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width + + draw_texture_state.src_x0; + draw_texture_state.src_y1 = + (static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height + + draw_texture_state.src_y0; + draw_texture_state.src_sampler = regs.draw_texture.src_sampler; + draw_texture_state.src_texture = regs.draw_texture.src_texture; + + maxwell3d->rasterizer->DrawTexture(); +} + void DrawManager::UpdateTopology() { const auto& regs{maxwell3d->regs}; switch (regs.primitive_topology_control) { @@ -198,4 +246,18 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) { maxwell3d->rasterizer->Draw(draw_indexed, instance_count); } } + +void DrawManager::ProcessDrawIndirect() { + LOG_TRACE( + HW_GPU, + "called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}", + draw_state.topology, indirect_state.is_indexed, indirect_state.include_count, + indirect_state.buffer_size, indirect_state.max_draw_counts); + + UpdateTopology(); + + if (maxwell3d->ShouldExecute()) { + maxwell3d->rasterizer->DrawIndirect(); + } +} } // namespace Tegra::Engines diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h index 0e6930a9c..7c22c49f1 100644 --- a/src/video_core/engines/draw_manager.h +++ b/src/video_core/engines/draw_manager.h @@ -32,6 +32,29 @@ public: std::vector<u8> inline_index_draw_indexes; }; + struct DrawTextureState { + f32 dst_x0; + f32 dst_y0; + f32 dst_x1; + f32 dst_y1; + f32 src_x0; + f32 src_y0; + f32 src_x1; + f32 src_y1; + u32 src_sampler; + u32 src_texture; + }; + + struct IndirectParams { + bool is_indexed; + bool include_count; + GPUVAddr count_start_address; + GPUVAddr indirect_start_address; + size_t buffer_size; + size_t max_draw_counts; + size_t stride; + }; + explicit DrawManager(Maxwell3D* maxwell_3d); void ProcessMethodCall(u32 method, u32 argument); @@ -46,10 +69,26 @@ public: void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances); + void DrawArrayIndirect(PrimitiveTopology topology); + + void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count); + const State& GetDrawState() const { return draw_state; } + const DrawTextureState& GetDrawTextureState() const { + return draw_texture_state; + } + + IndirectParams& GetIndirectParams() { + return indirect_state; + } + + const IndirectParams& GetIndirectParams() const { + return indirect_state; + } + private: void SetInlineIndexBuffer(u32 index); @@ -59,11 +98,17 @@ private: void DrawIndexSmall(u32 argument); + void DrawTexture(); + void UpdateTopology(); void ProcessDraw(bool draw_indexed, u32 instance_count); + void ProcessDrawIndirect(); + Maxwell3D* maxwell3d{}; State draw_state{}; + DrawTextureState draw_texture_state{}; + IndirectParams indirect_state{}; }; } // namespace Tegra::Engines diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index 26cde8584..392322358 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h @@ -3,6 +3,10 @@ #pragma once +#include <bitset> +#include <limits> +#include <vector> + #include "common/common_types.h" namespace Tegra::Engines { @@ -17,6 +21,26 @@ public: /// Write multiple values to the register identified by method. virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0; + + void ConsumeSink() { + if (method_sink.empty()) { + return; + } + ConsumeSinkImpl(); + } + + std::bitset<std::numeric_limits<u16>::max()> execution_mask{}; + std::vector<std::pair<u32, u32>> method_sink{}; + bool current_dirty{}; + GPUVAddr current_dma_segment; + +protected: + virtual void ConsumeSinkImpl() { + for (auto [method, value] : method_sink) { + CallMethod(method, value, true); + } + method_sink.clear(); + } }; } // namespace Tegra::Engines diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index cea1dd8b0..7f5a0c29d 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp @@ -76,7 +76,7 @@ void State::ProcessData(std::span<const u8> read_buffer) { regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, x_elements, regs.line_count, regs.dest.BlockHeight(), regs.dest.BlockDepth(), regs.line_length_in); - memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); + memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size); } } diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index c6478ae85..a126c359c 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -6,6 +6,7 @@ #include "common/microprofile.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/sw_blitter/blitter.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/surface.h" #include "video_core/textures/decoders.h" @@ -20,11 +21,14 @@ namespace Tegra::Engines { using namespace Texture; -Fermi2D::Fermi2D(MemoryManager& memory_manager_) { - sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_); +Fermi2D::Fermi2D(MemoryManager& memory_manager_) : memory_manager{memory_manager_} { + sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager); // Nvidia's OpenGL driver seems to assume these values regs.src.depth = 1; regs.dst.depth = 1; + + execution_mask.reset(); + execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true; } Fermi2D::~Fermi2D() = default; @@ -49,6 +53,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 } } +void Fermi2D::ConsumeSinkImpl() { + for (auto [method, value] : method_sink) { + regs.reg_array[method] = value; + } + method_sink.clear(); +} + void Fermi2D::Blit() { MICROPROFILE_SCOPE(GPU_BlitEngine); LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", @@ -94,6 +105,7 @@ void Fermi2D::Blit() { config.src_x0 = 0; } + memory_manager.FlushCaching(); if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { sw_blitter->Blit(src, regs.dst, config); } diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 100b21bac..705b323e1 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -305,10 +305,13 @@ public: private: VideoCore::RasterizerInterface* rasterizer = nullptr; std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter; + MemoryManager& memory_manager; /// Performs the copy from the source surface to the destination surface as configured in the /// registers. void Blit(); + + void ConsumeSinkImpl() override; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index e5c622155..601095f03 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -14,7 +14,12 @@ namespace Tegra::Engines { KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) - : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {} + : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} { + execution_mask.reset(); + execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true; + execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true; + execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true; +} KeplerCompute::~KeplerCompute() = default; @@ -23,6 +28,13 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) upload_state.BindRasterizer(rasterizer); } +void KeplerCompute::ConsumeSinkImpl() { + for (auto [method, value] : method_sink) { + regs.reg_array[method] = value; + } + method_sink.clear(); +} + void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure"); diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index e154e3f06..2092e685f 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -204,6 +204,8 @@ public: private: void ProcessLaunch(); + void ConsumeSinkImpl() override; + /// Retrieves information about a specific TIC entry from the TIC buffer. Texture::TICEntry GetTICEntry(u32 tic_index) const; diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 08045d1cf..c026801a3 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -18,6 +18,17 @@ KeplerMemory::~KeplerMemory() = default; void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { upload_state.BindRasterizer(rasterizer_); + + execution_mask.reset(); + execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true; + execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true; +} + +void KeplerMemory::ConsumeSinkImpl() { + for (auto [method, value] : method_sink) { + regs.reg_array[method] = value; + } + method_sink.clear(); } void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 5fe7489f0..fb1eecbba 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -73,6 +73,8 @@ public: } regs{}; private: + void ConsumeSinkImpl() override; + Core::System& system; Upload::State upload_state; }; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 9b182b653..ae9da6290 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -4,6 +4,8 @@ #include <cstring> #include <optional> #include "common/assert.h" +#include "common/scope_exit.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "video_core/dirty_flags.h" @@ -28,6 +30,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_) regs.upload} { dirty.flags.flip(); InitializeRegisterDefaults(); + execution_mask.reset(); + for (size_t i = 0; i < execution_mask.size(); i++) { + execution_mask[i] = IsMethodExecutable(static_cast<u32>(i)); + } } Maxwell3D::~Maxwell3D() = default; @@ -121,6 +127,72 @@ void Maxwell3D::InitializeRegisterDefaults() { shadow_state = regs; } +bool Maxwell3D::IsMethodExecutable(u32 method) { + if (method >= MacroRegistersStart) { + return true; + } + switch (method) { + case MAXWELL3D_REG_INDEX(draw.end): + case MAXWELL3D_REG_INDEX(draw.begin): + case MAXWELL3D_REG_INDEX(vertex_buffer.first): + case MAXWELL3D_REG_INDEX(vertex_buffer.count): + case MAXWELL3D_REG_INDEX(index_buffer.first): + case MAXWELL3D_REG_INDEX(index_buffer.count): + case MAXWELL3D_REG_INDEX(draw_inline_index): + case MAXWELL3D_REG_INDEX(index_buffer32_subsequent): + case MAXWELL3D_REG_INDEX(index_buffer16_subsequent): + case MAXWELL3D_REG_INDEX(index_buffer8_subsequent): + case MAXWELL3D_REG_INDEX(index_buffer32_first): + case MAXWELL3D_REG_INDEX(index_buffer16_first): + case MAXWELL3D_REG_INDEX(index_buffer8_first): + case MAXWELL3D_REG_INDEX(inline_index_2x16.even): + case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): + case MAXWELL3D_REG_INDEX(vertex_array_instance_first): + case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): + case MAXWELL3D_REG_INDEX(draw_texture.src_y0): + case MAXWELL3D_REG_INDEX(wait_for_idle): + case MAXWELL3D_REG_INDEX(shadow_ram_control): + case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): + case MAXWELL3D_REG_INDEX(load_mme.instruction): + case MAXWELL3D_REG_INDEX(load_mme.start_address): + case MAXWELL3D_REG_INDEX(falcon[4]): + case MAXWELL3D_REG_INDEX(const_buffer.buffer): + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: + case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config): + case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config): + case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config): + case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config): + case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): + case MAXWELL3D_REG_INDEX(topology_override): + case MAXWELL3D_REG_INDEX(clear_surface): + case MAXWELL3D_REG_INDEX(report_semaphore.query): + case MAXWELL3D_REG_INDEX(render_enable.mode): + case MAXWELL3D_REG_INDEX(clear_report_value): + case MAXWELL3D_REG_INDEX(sync_info): + case MAXWELL3D_REG_INDEX(launch_dma): + case MAXWELL3D_REG_INDEX(inline_data): + case MAXWELL3D_REG_INDEX(fragment_barrier): + case MAXWELL3D_REG_INDEX(tiled_cache_barrier): + return true; + default: + return false; + } +} + void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { if (executing_macro == 0) { // A macro call must begin by writing the macro method's register, not its argument. @@ -130,12 +202,70 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool } macro_params.insert(macro_params.end(), base_start, base_start + amount); + for (size_t i = 0; i < amount; i++) { + macro_addresses.push_back(current_dma_segment + i * sizeof(u32)); + } + macro_segments.emplace_back(current_dma_segment, amount); + current_macro_dirty |= current_dirty; + current_dirty = false; // Call the macro when there are no more parameters in the command buffer if (is_last_call) { + ConsumeSink(); CallMacroMethod(executing_macro, macro_params); macro_params.clear(); + macro_addresses.clear(); + macro_segments.clear(); + current_macro_dirty = false; + } +} + +void Maxwell3D::RefreshParametersImpl() { + size_t current_index = 0; + for (auto& segment : macro_segments) { + if (segment.first == 0) { + current_index += segment.second; + continue; + } + memory_manager.ReadBlock(segment.first, ¯o_params[current_index], + sizeof(u32) * segment.second); + current_index += segment.second; + } +} + +u32 Maxwell3D::GetMaxCurrentVertices() { + u32 num_vertices = 0; + for (size_t index = 0; index < Regs::NumVertexArrays; ++index) { + const auto& array = regs.vertex_streams[index]; + if (array.enable == 0) { + continue; + } + const auto& attribute = regs.vertex_attrib_format[index]; + if (attribute.constant) { + num_vertices = std::max(num_vertices, 1U); + continue; + } + const auto& limit = regs.vertex_stream_limits[index]; + const GPUVAddr gpu_addr_begin = array.Address(); + const GPUVAddr gpu_addr_end = limit.Address() + 1; + const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); + num_vertices = std::max( + num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); } + return num_vertices; +} + +size_t Maxwell3D::EstimateIndexBufferSize() { + GPUVAddr start_address = regs.index_buffer.StartAddress(); + GPUVAddr end_address = regs.index_buffer.EndAddress(); + constexpr std::array<size_t, 4> max_sizes = { + std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(), + std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()}; + const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); + return std::min<size_t>( + memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) / + byte_size, + static_cast<size_t>(end_address - start_address)); } u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { @@ -152,6 +282,29 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { return argument; } +void Maxwell3D::ConsumeSinkImpl() { + SCOPE_EXIT({ method_sink.clear(); }); + const auto control = shadow_state.shadow_ram_control; + if (control == Regs::ShadowRamControl::Track || + control == Regs::ShadowRamControl::TrackWithFilter) { + + for (auto [method, value] : method_sink) { + shadow_state.reg_array[method] = value; + ProcessDirtyRegisters(method, value); + } + return; + } + if (control == Regs::ShadowRamControl::Replay) { + for (auto [method, value] : method_sink) { + ProcessDirtyRegisters(method, shadow_state.reg_array[method]); + } + return; + } + for (auto [method, value] : method_sink) { + ProcessDirtyRegisters(method, value); + } +} + void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { if (regs.reg_array[method] == argument) { return; @@ -263,7 +416,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { const u32 argument = ProcessShadowRam(method, method_argument); ProcessDirtyRegisters(method, argument); - ProcessMethodCall(method, argument, method_argument, is_last_call); } @@ -294,9 +446,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: ProcessCBMultiData(base_start, amount); break; - case MAXWELL3D_REG_INDEX(inline_data): + case MAXWELL3D_REG_INDEX(inline_data): { + ASSERT(methods_pending == amount); upload_state.ProcessData(base_start, amount); return; + } default: for (u32 i = 0; i < amount; i++) { CallMethod(method, base_start[i], methods_pending - i <= 1); @@ -314,7 +468,7 @@ void Maxwell3D::ProcessMacroBind(u32 data) { } void Maxwell3D::ProcessFirmwareCall4() { - LOG_WARNING(HW_GPU, "(STUBBED) called"); + LOG_DEBUG(HW_GPU, "(STUBBED) called"); // Firmware call 4 is a blob that changes some registers depending on its parameters. // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. @@ -332,11 +486,6 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { } void Maxwell3D::ProcessQueryGet() { - // TODO(Subv): Support the other query units. - if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) { - LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented"); - } - switch (regs.report_semaphore.query.operation) { case Regs::ReportSemaphore::Operation::Release: if (regs.report_semaphore.query.short_query != 0) { @@ -389,7 +538,11 @@ void Maxwell3D::ProcessQueryCondition() { case Regs::RenderEnable::Override::NeverRender: execute_on = false; break; - case Regs::RenderEnable::Override::UseRenderEnable: + case Regs::RenderEnable::Override::UseRenderEnable: { + if (rasterizer->AccelerateConditionalRendering()) { + execute_on = true; + return; + } switch (regs.render_enable.mode) { case Regs::RenderEnable::Mode::True: { execute_on = true; @@ -427,6 +580,7 @@ void Maxwell3D::ProcessQueryCondition() { } break; } + } } void Maxwell3D::ProcessCounterReset() { @@ -463,7 +617,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() { } void Maxwell3D::ProcessCBBind(size_t stage_index) { - // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. + // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader + // stage. const auto& bind_data = regs.bind_groups[stage_index]; auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; buffer.enabled = bind_data.valid.Value() != 0; @@ -490,7 +645,7 @@ void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) { const GPUVAddr address{buffer_address + regs.const_buffer.offset}; const size_t copy_size = amount * sizeof(u32); - memory_manager.WriteBlock(address, start_base, copy_size); + memory_manager.WriteBlockCached(address, start_base, copy_size); // Increment the current buffer position. regs.const_buffer.offset += static_cast<u32>(copy_size); @@ -524,4 +679,10 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const { return regs.reg_array[method]; } +void Maxwell3D::SetHLEReplacementAttributeType(u32 bank, u32 offset, + HLEReplacementAttributeType name) { + const u64 key = (static_cast<u64>(bank) << 32) | offset; + replace_table.emplace(key, name); +} + } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 22b904319..c89969bb4 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -272,6 +272,7 @@ public: }; union { + u32 raw; BitField<0, 1, Mode> mode; BitField<4, 8, u32> pad; }; @@ -1217,10 +1218,12 @@ public: struct Window { union { + u32 raw_x; BitField<0, 16, u32> x_min; BitField<16, 16, u32> x_max; }; union { + u32 raw_y; BitField<0, 16, u32> y_min; BitField<16, 16, u32> y_max; }; @@ -1596,6 +1599,20 @@ public: }; static_assert(sizeof(TIRModulationCoeff) == 0x4); + struct DrawTexture { + s32 dst_x0; + s32 dst_y0; + s32 dst_width; + s32 dst_height; + s64 dx_du; + s64 dy_dv; + u32 src_sampler; + u32 src_texture; + s32 src_x0; + s32 src_y0; + }; + static_assert(sizeof(DrawTexture) == 0x30); + struct ReduceColorThreshold { union { BitField<0, 8, u32> all_hit_once; @@ -2708,7 +2725,7 @@ public: u32 post_z_pixel_imask; ///< 0x0F1C INSERT_PADDING_BYTES_NOINIT(0x20); ConstantColorRendering const_color_rendering; ///< 0x0F40 - s32 stencil_back_ref; ///< 0x0F54 + u32 stencil_back_ref; ///< 0x0F54 u32 stencil_back_mask; ///< 0x0F58 u32 stencil_back_func_mask; ///< 0x0F5C INSERT_PADDING_BYTES_NOINIT(0x14); @@ -2748,7 +2765,7 @@ public: u32 reserved_sw_method2; ///< 0x102C std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030 std::array<u32, 15> spare_nop; ///< 0x1044 - INSERT_PADDING_BYTES_NOINIT(0x30); + DrawTexture draw_texture; ///< 0x1080 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0 @@ -2832,9 +2849,9 @@ public: Blend blend; ///< 0x133C u32 stencil_enable; ///< 0x1380 StencilOp stencil_front_op; ///< 0x1384 - s32 stencil_front_ref; ///< 0x1394 - s32 stencil_front_func_mask; ///< 0x1398 - s32 stencil_front_mask; ///< 0x139C + u32 stencil_front_ref; ///< 0x1394 + u32 stencil_front_func_mask; ///< 0x1398 + u32 stencil_front_mask; ///< 0x139C INSERT_PADDING_BYTES_NOINIT(0x4); u32 draw_auto_start_byte_count; ///< 0x13A4 PsSaturate frag_color_clamp; ///< 0x13A8 @@ -3020,6 +3037,24 @@ public: /// Store temporary hw register values, used by some calls to restore state after a operation Regs shadow_state; + // None Engine + enum class EngineHint : u32 { + None = 0x0, + OnHLEMacro = 0x1, + }; + + EngineHint engine_state{EngineHint::None}; + + enum class HLEReplacementAttributeType : u32 { + BaseVertex = 0x0, + BaseInstance = 0x1, + DrawID = 0x2, + }; + + void SetHLEReplacementAttributeType(u32 bank, u32 offset, HLEReplacementAttributeType name); + + std::unordered_map<u64, HLEReplacementAttributeType> replace_table; + static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable"); @@ -3067,6 +3102,35 @@ public: std::unique_ptr<DrawManager> draw_manager; friend class DrawManager; + GPUVAddr GetMacroAddress(size_t index) const { + return macro_addresses[index]; + } + + void RefreshParameters() { + if (!current_macro_dirty) { + return; + } + RefreshParametersImpl(); + } + + bool AnyParametersDirty() const { + return current_macro_dirty; + } + + u32 GetMaxCurrentVertices(); + + size_t EstimateIndexBufferSize(); + + /// Handles a write to the CLEAR_BUFFERS register. + void ProcessClearBuffers(u32 layer_count); + + /// Handles a write to the CB_BIND register. + void ProcessCBBind(size_t stage_index); + + /// Handles a write to the CB_DATA[i] register. + void ProcessCBData(u32 value); + void ProcessCBMultiData(const u32* start_base, u32 amount); + private: void InitializeRegisterDefaults(); @@ -3076,6 +3140,8 @@ private: void ProcessDirtyRegisters(u32 method, u32 argument); + void ConsumeSinkImpl() override; + void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); /// Retrieves information about a specific TIC entry from the TIC buffer. @@ -3116,16 +3182,13 @@ private: /// Handles writes to syncing register. void ProcessSyncPoint(); - /// Handles a write to the CB_DATA[i] register. - void ProcessCBData(u32 value); - void ProcessCBMultiData(const u32* start_base, u32 amount); - - /// Handles a write to the CB_BIND register. - void ProcessCBBind(size_t stage_index); - /// Returns a query's value or an empty object if the value will be deferred through a cache. std::optional<u64> GetQueryResult(); + void RefreshParametersImpl(); + + bool IsMethodExecutable(u32 method); + Core::System& system; MemoryManager& memory_manager; @@ -3145,6 +3208,10 @@ private: Upload::State upload_state; bool execute_on{true}; + + std::vector<std::pair<GPUVAddr, size_t>> macro_segments; + std::vector<GPUVAddr> macro_addresses; + bool current_macro_dirty{}; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index f73d7bf0f..7762c7d96 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -21,7 +21,10 @@ namespace Tegra::Engines { using namespace Texture; MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) - : system{system_}, memory_manager{memory_manager_} {} + : system{system_}, memory_manager{memory_manager_} { + execution_mask.reset(); + execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true; +} MaxwellDMA::~MaxwellDMA() = default; @@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; } +void MaxwellDMA::ConsumeSinkImpl() { + for (auto [method, value] : method_sink) { + regs.reg_array[method] = value; + } + method_sink.clear(); +} + void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); @@ -59,7 +69,7 @@ void MaxwellDMA::Launch() { if (launch.multi_line_enable) { const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; - + memory_manager.FlushCaching(); if (!is_src_pitch && !is_dst_pitch) { // If both the source and the destination are in block layout, assert. CopyBlockLinearToBlockLinear(); @@ -94,6 +104,7 @@ void MaxwellDMA::Launch() { reinterpret_cast<u8*>(tmp_buffer.data()), regs.line_length_in * sizeof(u32)); } else { + memory_manager.FlushCaching(); const auto convert_linear_2_blocklinear_addr = [](u64 address) { return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | ((address & 0x180) >> 1) | ((address & 0x20) << 3); @@ -111,8 +122,8 @@ void MaxwellDMA::Launch() { memory_manager.ReadBlockUnsafe( convert_linear_2_blocklinear_addr(regs.offset_in + offset), tmp_buffer.data(), tmp_buffer.size()); - memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), - tmp_buffer.size()); + memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(), + tmp_buffer.size()); } } else if (is_src_pitch && !is_dst_pitch) { UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); @@ -122,7 +133,7 @@ void MaxwellDMA::Launch() { for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), tmp_buffer.size()); - memory_manager.WriteBlock( + memory_manager.WriteBlockCached( convert_linear_2_blocklinear_addr(regs.offset_out + offset), tmp_buffer.data(), tmp_buffer.size()); } @@ -131,8 +142,8 @@ void MaxwellDMA::Launch() { std::vector<u8> tmp_buffer(regs.line_length_in); memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); - memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), - regs.line_length_in); + memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(), + regs.line_length_in); } } } @@ -194,7 +205,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, regs.pitch_out); - memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); + memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::CopyPitchToBlockLinear() { @@ -246,7 +257,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() { dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, regs.pitch_in); - memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); + memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::FastCopyBlockLinearToPitch() { @@ -277,7 +288,7 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { regs.src_params.block_size.height, regs.src_params.block_size.depth, regs.pitch_out); - memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); + memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::CopyBlockLinearToBlockLinear() { @@ -337,7 +348,7 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() { dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, dst.block_size.height, dst.block_size.depth, pitch); - memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); + memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::ReleaseSemaphore() { diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index c88191a61..0e594fa74 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -231,6 +231,8 @@ private: void ReleaseSemaphore(); + void ConsumeSinkImpl() override; + Core::System& system; MemoryManager& memory_manager; diff --git a/src/video_core/fsr.cpp b/src/video_core/fsr.cpp new file mode 100644 index 000000000..5653c64fc --- /dev/null +++ b/src/video_core/fsr.cpp @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <cmath> +#include "video_core/fsr.h" + +namespace FSR { +namespace { +// Reimplementations of the constant generating functions in ffx_fsr1.h +// GCC generated a lot of warnings when using the official header. +u32 AU1_AH1_AF1(f32 f) { + static constexpr u32 base[512]{ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, + 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000, + 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00, + 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, + 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008, + 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400, + 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, + 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, + 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, + }; + static constexpr s8 shift[512]{ + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, + 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, + }; + const u32 u = Common::BitCast<u32>(f); + const u32 i = u >> 23; + return base[i] + ((u & 0x7fffff) >> shift[i]); +} + +u32 AU1_AH2_AF2(f32 a[2]) { + return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16); +} + +void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX, + f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, + f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) { + con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX); + con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY); + con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f); + con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f); + con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); + con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY); + con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); + con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY); + con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX); + con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY); + con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); + con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY); + con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX); + con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY); + con3[2] = con3[3] = 0; +} +} // Anonymous namespace + +void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], + f32 inputViewportInPixelsX, f32 inputViewportInPixelsY, + f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX, + f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) { + FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, + inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); + con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f + + inputOffsetInPixelsX); + con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f + + inputOffsetInPixelsY); +} + +void FsrRcasCon(u32* con, f32 sharpness) { + sharpness = std::exp2f(-sharpness); + f32 hSharp[2]{sharpness, sharpness}; + con[0] = Common::BitCast<u32>(sharpness); + con[1] = AU1_AH2_AF2(hSharp); + con[2] = 0; + con[3] = 0; +} +} // namespace FSR diff --git a/src/video_core/fsr.h b/src/video_core/fsr.h new file mode 100644 index 000000000..db0d4ec6f --- /dev/null +++ b/src/video_core/fsr.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/bit_cast.h" +#include "common/common_types.h" + +namespace FSR { +// Reimplementations of the constant generating functions in ffx_fsr1.h +// GCC generated a lot of warnings when using the official header. +void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], + f32 inputViewportInPixelsX, f32 inputViewportInPixelsY, + f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX, + f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY); + +void FsrRcasCon(u32* con, f32 sharpness); + +} // namespace FSR diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index e6dc24f22..52cd5bb81 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -3,14 +3,19 @@ set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) -set(GLSL_INCLUDES - fidelityfx_fsr.comp +set(FIDELITYFX_FILES ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h ) +set(GLSL_INCLUDES + fidelityfx_fsr.comp + ${FIDELITYFX_FILES} +) + set(SHADER_FILES astc_decoder.comp + blit_color_float.frag block_linear_unswizzle_2d.comp block_linear_unswizzle_3d.comp convert_abgr8_to_d24s8.frag @@ -23,6 +28,9 @@ set(SHADER_FILES fxaa.vert opengl_convert_s8d24.comp opengl_copy_bc4.comp + opengl_fidelityfx_fsr.frag + opengl_fidelityfx_fsr_easu.frag + opengl_fidelityfx_fsr_rcas.frag opengl_present.frag opengl_present.vert opengl_present_scaleforce.frag @@ -36,8 +44,9 @@ set(SHADER_FILES smaa_blending_weight_calculation.frag smaa_neighborhood_blending.vert smaa_neighborhood_blending.frag - vulkan_blit_color_float.frag vulkan_blit_depth_stencil.frag + vulkan_color_clear.frag + vulkan_color_clear.vert vulkan_fidelityfx_fsr_easu_fp16.comp vulkan_fidelityfx_fsr_easu_fp32.comp vulkan_fidelityfx_fsr_rcas_fp16.comp @@ -47,6 +56,7 @@ set(SHADER_FILES vulkan_present_scaleforce_fp16.frag vulkan_present_scaleforce_fp32.frag vulkan_quad_indexed.comp + vulkan_turbo_mode.comp vulkan_uint8.comp ) @@ -117,6 +127,25 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES}) endif() endforeach() +foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES}) + get_filename_component(FILENAME ${FILEPATH} NAME) + string(REPLACE "." "_" HEADER_NAME ${FILENAME}) + set(SOURCE_FILE ${FILEPATH}) + set(SOURCE_HEADER_FILE ${SHADER_DIR}/${HEADER_NAME}.h) + add_custom_command( + OUTPUT + ${SOURCE_HEADER_FILE} + COMMAND + ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE} + MAIN_DEPENDENCY + ${SOURCE_FILE} + DEPENDS + ${INPUT_FILE} + # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE}) +endforeach() + set(SHADER_SOURCES ${SHADER_FILES}) list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) diff --git a/src/video_core/host_shaders/vulkan_blit_color_float.frag b/src/video_core/host_shaders/blit_color_float.frag index c0c832296..c0c832296 100644 --- a/src/video_core/host_shaders/vulkan_blit_color_float.frag +++ b/src/video_core/host_shaders/blit_color_float.frag diff --git a/src/video_core/host_shaders/full_screen_triangle.vert b/src/video_core/host_shaders/full_screen_triangle.vert index 2c976b19f..d16d98995 100644 --- a/src/video_core/host_shaders/full_screen_triangle.vert +++ b/src/video_core/host_shaders/full_screen_triangle.vert @@ -4,13 +4,20 @@ #version 450 #ifdef VULKAN +#define VERTEX_ID gl_VertexIndex #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define END_PUSH_CONSTANTS }; #define UNIFORM(n) +#define FLIPY 1 #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv +#define VERTEX_ID gl_VertexID #define BEGIN_PUSH_CONSTANTS #define END_PUSH_CONSTANTS +#define FLIPY -1 #define UNIFORM(n) layout (location = n) uniform +out gl_PerVertex { + vec4 gl_Position; +}; #endif BEGIN_PUSH_CONSTANTS @@ -21,8 +28,8 @@ END_PUSH_CONSTANTS layout(location = 0) out vec2 texcoord; void main() { - float x = float((gl_VertexIndex & 1) << 2); - float y = float((gl_VertexIndex & 2) << 1); - gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); + float x = float((VERTEX_ID & 1) << 2); + float y = float((VERTEX_ID & 2) << 1); + gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0); texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset); } diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag new file mode 100644 index 000000000..16d22f58e --- /dev/null +++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +//!#version 460 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#extension GL_AMD_gpu_shader_half_float : enable +#extension GL_NV_gpu_shader5 : enable + +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +layout (location = 0) uniform uvec4 constants[4]; + +#define A_GPU 1 +#define A_GLSL 1 + +#ifdef YUZU_USE_FP16 + #define A_HALF +#endif +#include "ffx_a.h" + +#ifndef YUZU_USE_FP16 + layout (binding=0) uniform sampler2D InputTexture; + #if USE_EASU + #define FSR_EASU_F 1 + AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; } + AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; } + AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; } + #endif + #if USE_RCAS + #define FSR_RCAS_F + AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); } + void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {} + #endif +#else + layout (binding=0) uniform sampler2D InputTexture; + #if USE_EASU + #define FSR_EASU_H 1 + AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; } + AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; } + AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; } + #endif + #if USE_RCAS + #define FSR_RCAS_H + AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); } + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){} + #endif +#endif + +#include "ffx_fsr1.h" + +#if USE_RCAS + layout(location = 0) in vec2 frag_texcoord; +#endif +layout (location = 0) out vec4 frag_color; + +void CurrFilter(AU2 pos) +{ +#if USE_EASU + #ifndef YUZU_USE_FP16 + AF3 c; + FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]); + frag_color = AF4(c, 1.0); + #else + AH3 c; + FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]); + frag_color = AH4(c, 1.0); + #endif +#endif +#if USE_RCAS + #ifndef YUZU_USE_FP16 + AF3 c; + FsrRcasF(c.r, c.g, c.b, pos, constants[0]); + frag_color = AF4(c, 1.0); + #else + AH3 c; + FsrRcasH(c.r, c.g, c.b, pos, constants[0]); + frag_color = AH4(c, 1.0); + #endif +#endif +} + +void main() +{ +#if USE_RCAS + CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0)))); +#else + CurrFilter(AU2(gl_FragCoord.xy)); +#endif +} diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag new file mode 100644 index 000000000..d39f80ac1 --- /dev/null +++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 core +#extension GL_GOOGLE_include_directive : enable + +#define USE_EASU 1 + +#include "opengl_fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag new file mode 100644 index 000000000..cfa78ddc7 --- /dev/null +++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 core +#extension GL_GOOGLE_include_directive : enable + +#define USE_RCAS 1 + +#include "opengl_fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_color_clear.frag b/src/video_core/host_shaders/vulkan_color_clear.frag new file mode 100644 index 000000000..617bf01e1 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_color_clear.frag @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 core + +layout (push_constant) uniform PushConstants { + vec4 clear_color; +}; + +layout(location = 0) out vec4 color; + +void main() { + color = clear_color; +} diff --git a/src/video_core/host_shaders/vulkan_color_clear.vert b/src/video_core/host_shaders/vulkan_color_clear.vert new file mode 100644 index 000000000..d85883141 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_color_clear.vert @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 core + +void main() { + float x = float((gl_VertexIndex & 1) << 2); + float y = float((gl_VertexIndex & 2) << 1); + gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); +} diff --git a/src/video_core/host_shaders/vulkan_quad_indexed.comp b/src/video_core/host_shaders/vulkan_quad_indexed.comp index a412f30ff..066fe4a9c 100644 --- a/src/video_core/host_shaders/vulkan_quad_indexed.comp +++ b/src/video_core/host_shaders/vulkan_quad_indexed.comp @@ -16,6 +16,7 @@ layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer { layout (push_constant) uniform PushConstants { uint base_vertex; int index_shift; // 0: uint8, 1: uint16, 2: uint32 + int is_strip; // 0: quads 1: quadstrip }; void main() { @@ -28,9 +29,10 @@ void main() { int flipped_shift = 2 - index_shift; int mask = (1 << flipped_shift) - 1; - const int quad_swizzle[6] = int[](0, 1, 2, 0, 2, 3); + const int quads_swizzle[6] = int[](0, 1, 2, 0, 2, 3); + const int quad_strip_swizzle[6] = int[](0, 3, 1, 0, 2, 3); for (uint vertex = 0; vertex < 6; ++vertex) { - int offset = primitive * 4 + quad_swizzle[vertex]; + int offset = (is_strip == 0 ? primitive * 4 + quads_swizzle[vertex] : primitive * 2 + quad_strip_swizzle[vertex]); int int_offset = offset >> flipped_shift; int bit_offset = (offset & mask) * index_size; uint packed_input = input_indexes[int_offset]; diff --git a/src/video_core/host_shaders/vulkan_turbo_mode.comp b/src/video_core/host_shaders/vulkan_turbo_mode.comp new file mode 100644 index 000000000..d651001d9 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_turbo_mode.comp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 core + +layout (local_size_x = 16, local_size_y = 8, local_size_z = 1) in; + +layout (binding = 0) buffer ThreadData { + uint data[]; +}; + +uint xorshift32(uint x) { + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} + +uint getGlobalIndex() { + return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_WorkGroupSize.y * gl_NumWorkGroups.y; +} + +void main() { + uint myIndex = xorshift32(getGlobalIndex()); + uint otherIndex = xorshift32(myIndex); + + uint otherValue = atomicAdd(data[otherIndex % data.length()], 0) + 1; + atomicAdd(data[myIndex % data.length()], otherValue); +} diff --git a/src/video_core/invalidation_accumulator.h b/src/video_core/invalidation_accumulator.h new file mode 100644 index 000000000..2c2aaf7bb --- /dev/null +++ b/src/video_core/invalidation_accumulator.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <utility> +#include <vector> + +#include "common/common_types.h" + +namespace VideoCommon { + +class InvalidationAccumulator { +public: + InvalidationAccumulator() = default; + ~InvalidationAccumulator() = default; + + void Add(GPUVAddr address, size_t size) { + const auto reset_values = [&]() { + if (has_collected) { + buffer.emplace_back(start_address, accumulated_size); + } + start_address = address; + accumulated_size = size; + last_collection = start_address + size; + }; + if (address >= start_address && address + size <= last_collection) [[likely]] { + return; + } + size = ((address + size + atomicity_size_mask) & atomicity_mask) - address; + address = address & atomicity_mask; + if (!has_collected) [[unlikely]] { + reset_values(); + has_collected = true; + return; + } + if (address != last_collection) [[unlikely]] { + reset_values(); + return; + } + accumulated_size += size; + last_collection += size; + } + + void Clear() { + buffer.clear(); + start_address = 0; + last_collection = 0; + has_collected = false; + } + + bool AnyAccumulated() const { + return has_collected; + } + + template <typename Func> + void Callback(Func&& func) { + if (!has_collected) { + return; + } + buffer.emplace_back(start_address, accumulated_size); + for (auto& [address, size] : buffer) { + func(address, size); + } + } + +private: + static constexpr size_t atomicity_bits = 5; + static constexpr size_t atomicity_size = 1ULL << atomicity_bits; + static constexpr size_t atomicity_size_mask = atomicity_size - 1; + static constexpr size_t atomicity_mask = ~atomicity_size_mask; + GPUVAddr start_address{}; + GPUVAddr last_collection{}; + size_t accumulated_size{}; + bool has_collected{}; + std::vector<std::pair<VAddr, size_t>> buffer; +}; + +} // namespace VideoCommon diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 505d81c1e..82ad0477d 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -12,7 +12,9 @@ #include "common/assert.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/microprofile.h" #include "common/settings.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_interpreter.h" @@ -21,6 +23,8 @@ #include "video_core/macro/macro_jit_x64.h" #endif +MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192)); + namespace Tegra { static void Dump(u64 hash, std::span<const u32> code) { @@ -40,8 +44,8 @@ static void Dump(u64 hash, std::span<const u32> code) { macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes()); } -MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) - : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} +MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d_) + : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d_)}, maxwell3d{maxwell3d_} {} MacroEngine::~MacroEngine() = default; @@ -59,8 +63,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { if (compiled_macro != macro_cache.end()) { const auto& cache_info = compiled_macro->second; if (cache_info.has_hle_program) { + MICROPROFILE_SCOPE(MacroHLE); cache_info.hle_program->Execute(parameters, method); } else { + maxwell3d.RefreshParameters(); cache_info.lle_program->Execute(parameters, method); } } else { @@ -101,12 +107,15 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { } } - if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { + auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); + if (!hle_program || Settings::values.disable_macro_hle) { + maxwell3d.RefreshParameters(); + cache_info.lle_program->Execute(parameters, method); + } else { cache_info.has_hle_program = true; cache_info.hle_program = std::move(hle_program); + MICROPROFILE_SCOPE(MacroHLE); cache_info.hle_program->Execute(parameters, method); - } else { - cache_info.lle_program->Execute(parameters, method); } } } diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h index 07d97ba39..737ced9a4 100644 --- a/src/video_core/macro/macro.h +++ b/src/video_core/macro/macro.h @@ -137,6 +137,7 @@ private: std::unordered_map<u32, CacheInfo> macro_cache; std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; std::unique_ptr<HLEMacro> hle_macros; + Engines::Maxwell3D& maxwell3d; }; std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 8549db2e4..6272a4652 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -1,143 +1,551 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include <array> #include <vector> +#include "common/assert.h" #include "common/scope_exit.h" #include "video_core/dirty_flags.h" #include "video_core/engines/draw_manager.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Tegra { -namespace { -using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); +using Maxwell3D = Engines::Maxwell3D; -// HLE'd functions -void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { - const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); - maxwell3d.draw_manager->DrawIndex( - static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff), - parameters[4], parameters[1], parameters[3], parameters[5], instance_count); -} +namespace { -void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { - const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); - maxwell3d.draw_manager->DrawArray( - static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), - parameters[3], parameters[1], parameters[4], instance_count); +bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) { + switch (topology) { + case Maxwell3D::Regs::PrimitiveTopology::Points: + case Maxwell3D::Regs::PrimitiveTopology::Lines: + case Maxwell3D::Regs::PrimitiveTopology::LineLoop: + case Maxwell3D::Regs::PrimitiveTopology::LineStrip: + case Maxwell3D::Regs::PrimitiveTopology::Triangles: + case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip: + case Maxwell3D::Regs::PrimitiveTopology::TriangleFan: + case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency: + case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency: + case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: + case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency: + case Maxwell3D::Regs::PrimitiveTopology::Patches: + return true; + case Maxwell3D::Regs::PrimitiveTopology::Quads: + case Maxwell3D::Regs::PrimitiveTopology::QuadStrip: + case Maxwell3D::Regs::PrimitiveTopology::Polygon: + default: + return false; + } } -void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { - const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); - const u32 element_base = parameters[4]; - const u32 base_instance = parameters[5]; - maxwell3d.regs.vertex_id_base = element_base; - maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - maxwell3d.CallMethod(0x8e3, 0x640, true); - maxwell3d.CallMethod(0x8e4, element_base, true); - maxwell3d.CallMethod(0x8e5, base_instance, true); - - maxwell3d.draw_manager->DrawIndex( - static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), - parameters[3], parameters[1], element_base, base_instance, instance_count); - - maxwell3d.regs.vertex_id_base = 0x0; - maxwell3d.CallMethod(0x8e3, 0x640, true); - maxwell3d.CallMethod(0x8e4, 0x0, true); - maxwell3d.CallMethod(0x8e5, 0x0, true); -} +class HLEMacroImpl : public CachedMacro { +public: + explicit HLEMacroImpl(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} -// Multidraw Indirect -void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { - SCOPE_EXIT({ - // Clean everything. - maxwell3d.regs.vertex_id_base = 0x0; - maxwell3d.CallMethod(0x8e3, 0x640, true); - maxwell3d.CallMethod(0x8e4, 0x0, true); - maxwell3d.CallMethod(0x8e5, 0x0, true); +protected: + Maxwell3D& maxwell3d; +}; + +/* + * @note: these macros have two versions, a normal and extended version, with the extended version + * also assigning the base vertex/instance. + */ +template <bool extended> +class HLE_DrawArraysIndirect final : public HLEMacroImpl { +public: + explicit HLE_DrawArraysIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]); + if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { + Fallback(parameters); + return; + } + + auto& params = maxwell3d.draw_manager->GetIndirectParams(); + params.is_indexed = false; + params.include_count = false; + params.count_start_address = 0; + params.indirect_start_address = maxwell3d.GetMacroAddress(1); + params.buffer_size = 4 * sizeof(u32); + params.max_draw_counts = 1; + params.stride = 0; + + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + } + + maxwell3d.draw_manager->DrawArrayIndirect(topology); + + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } + } + +private: + void Fallback(const std::vector<u32>& parameters) { + SCOPE_EXIT({ + if (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } + }); + maxwell3d.RefreshParameters(); + const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); + + auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]); + const u32 vertex_first = parameters[3]; + const u32 vertex_count = parameters[1]; + + if (!IsTopologySafe(topology) && + static_cast<size_t>(maxwell3d.GetMaxCurrentVertices()) < + static_cast<size_t>(vertex_first) + static_cast<size_t>(vertex_count)) { + ASSERT_MSG(false, "Faulty draw!"); + return; + } + + const u32 base_instance = parameters[4]; + if constexpr (extended) { + maxwell3d.regs.global_base_instance_index = base_instance; + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + } + + maxwell3d.draw_manager->DrawArray(topology, vertex_first, vertex_count, base_instance, + instance_count); + + if constexpr (extended) { + maxwell3d.regs.global_base_instance_index = 0; + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } + } +}; + +/* + * @note: these macros have two versions, a normal and extended version, with the extended version + * also assigning the base vertex/instance. + */ +template <bool extended> +class HLE_DrawIndexedIndirect final : public HLEMacroImpl { +public: + explicit HLE_DrawIndexedIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]); + if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { + Fallback(parameters); + return; + } + + const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize()); + const u32 element_base = parameters[4]; + const u32 base_instance = parameters[5]; + maxwell3d.regs.vertex_id_base = element_base; + maxwell3d.regs.global_base_vertex_index = element_base; + maxwell3d.regs.global_base_instance_index = base_instance; + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + } + auto& params = maxwell3d.draw_manager->GetIndirectParams(); + params.is_indexed = true; + params.include_count = false; + params.count_start_address = 0; + params.indirect_start_address = maxwell3d.GetMacroAddress(1); + params.buffer_size = 5 * sizeof(u32); + params.max_draw_counts = 1; + params.stride = 0; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - }); - const u32 start_indirect = parameters[0]; - const u32 end_indirect = parameters[1]; - if (start_indirect >= end_indirect) { - // Nothing to do. - return; - } - const u32 padding = parameters[3]; - const std::size_t max_draws = parameters[4]; - - const u32 indirect_words = 5 + padding; - const std::size_t first_draw = start_indirect; - const std::size_t effective_draws = end_indirect - start_indirect; - const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); - - for (std::size_t index = first_draw; index < last_draw; index++) { - const std::size_t base = index * indirect_words + 5; - const u32 base_vertex = parameters[base + 3]; - const u32 base_instance = parameters[base + 4]; - maxwell3d.regs.vertex_id_base = base_vertex; - maxwell3d.CallMethod(0x8e3, 0x640, true); - maxwell3d.CallMethod(0x8e4, base_vertex, true); - maxwell3d.CallMethod(0x8e5, base_instance, true); + maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate); + maxwell3d.regs.vertex_id_base = 0x0; + maxwell3d.regs.global_base_vertex_index = 0x0; + maxwell3d.regs.global_base_instance_index = 0x0; + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } + } + +private: + void Fallback(const std::vector<u32>& parameters) { + maxwell3d.RefreshParameters(); + const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); + const u32 element_base = parameters[4]; + const u32 base_instance = parameters[5]; + maxwell3d.regs.vertex_id_base = element_base; + maxwell3d.regs.global_base_vertex_index = element_base; + maxwell3d.regs.global_base_instance_index = base_instance; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + } + maxwell3d.draw_manager->DrawIndex( - static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]), - parameters[base + 2], parameters[base], base_vertex, base_instance, - parameters[base + 1]); + static_cast<Tegra::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), parameters[3], + parameters[1], element_base, base_instance, instance_count); + + maxwell3d.regs.vertex_id_base = 0x0; + maxwell3d.regs.global_base_vertex_index = 0x0; + maxwell3d.regs.global_base_instance_index = 0x0; + if constexpr (extended) { + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } } -} +}; -// Multi-layer Clear -void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { - ASSERT(parameters.size() == 1); +class HLE_MultiLayerClear final : public HLEMacroImpl { +public: + explicit HLE_MultiLayerClear(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} - const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]}; - const u32 rt_index = clear_params.RT; - const u32 num_layers = maxwell3d.regs.rt[rt_index].depth; - ASSERT(clear_params.layer == 0); + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + ASSERT(parameters.size() == 1); - maxwell3d.regs.clear_surface.raw = clear_params.raw; - maxwell3d.draw_manager->Clear(num_layers); -} + const Maxwell3D::Regs::ClearSurface clear_params{parameters[0]}; + const u32 rt_index = clear_params.RT; + const u32 num_layers = maxwell3d.regs.rt[rt_index].depth; + ASSERT(clear_params.layer == 0); + + maxwell3d.regs.clear_surface.raw = clear_params.raw; + maxwell3d.draw_manager->Clear(num_layers); + } +}; + +class HLE_MultiDrawIndexedIndirectCount final : public HLEMacroImpl { +public: + explicit HLE_MultiDrawIndexedIndirectCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]); + if (!IsTopologySafe(topology)) { + Fallback(parameters); + return; + } + + const u32 start_indirect = parameters[0]; + const u32 end_indirect = parameters[1]; + if (start_indirect >= end_indirect) { + // Nothing to do. + return; + } -constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{ - {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, - {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, - {0x0217920100488FF7, &HLE_0217920100488FF7}, - {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164}, - {0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B}, -}}; + const u32 padding = parameters[3]; // padding is in words -class HLEMacroImpl final : public CachedMacro { + // size of each indirect segment + const u32 indirect_words = 5 + padding; + const u32 stride = indirect_words * sizeof(u32); + const std::size_t draw_count = end_indirect - start_indirect; + const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize()); + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + auto& params = maxwell3d.draw_manager->GetIndirectParams(); + params.is_indexed = true; + params.include_count = true; + params.count_start_address = maxwell3d.GetMacroAddress(4); + params.indirect_start_address = maxwell3d.GetMacroAddress(5); + params.buffer_size = stride * draw_count; + params.max_draw_counts = draw_count; + params.stride = stride; + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + maxwell3d.SetHLEReplacementAttributeType(0, 0x648, + Maxwell3D::HLEReplacementAttributeType::DrawID); + maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate); + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } + +private: + void Fallback(const std::vector<u32>& parameters) { + SCOPE_EXIT({ + // Clean everything. + maxwell3d.regs.vertex_id_base = 0x0; + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + }); + maxwell3d.RefreshParameters(); + const u32 start_indirect = parameters[0]; + const u32 end_indirect = parameters[1]; + if (start_indirect >= end_indirect) { + // Nothing to do. + return; + } + const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]); + const u32 padding = parameters[3]; + const std::size_t max_draws = parameters[4]; + + const u32 indirect_words = 5 + padding; + const std::size_t first_draw = start_indirect; + const std::size_t effective_draws = end_indirect - start_indirect; + const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); + + for (std::size_t index = first_draw; index < last_draw; index++) { + const std::size_t base = index * indirect_words + 5; + const u32 base_vertex = parameters[base + 3]; + const u32 base_instance = parameters[base + 4]; + maxwell3d.regs.vertex_id_base = base_vertex; + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); + maxwell3d.SetHLEReplacementAttributeType( + 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + maxwell3d.CallMethod(0x8e3, 0x648, true); + maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true); + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + maxwell3d.draw_manager->DrawIndex(topology, parameters[base + 2], parameters[base], + base_vertex, base_instance, parameters[base + 1]); + } + } +}; + +class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl { +public: + explicit HLE_C713C83D8F63CCF3(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2; + const u32 address = maxwell3d.regs.shadow_scratch[24]; + auto& const_buffer = maxwell3d.regs.const_buffer; + const_buffer.size = 0x7000; + const_buffer.address_high = (address >> 24) & 0xFF; + const_buffer.address_low = address << 8; + const_buffer.offset = offset; + } +}; + +class HLE_D7333D26E0A93EDE final : public HLEMacroImpl { +public: + explicit HLE_D7333D26E0A93EDE(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + const size_t index = parameters[0]; + const u32 address = maxwell3d.regs.shadow_scratch[42 + index]; + const u32 size = maxwell3d.regs.shadow_scratch[47 + index]; + auto& const_buffer = maxwell3d.regs.const_buffer; + const_buffer.size = size; + const_buffer.address_high = (address >> 24) & 0xFF; + const_buffer.address_low = address << 8; + } +}; + +class HLE_BindShader final : public HLEMacroImpl { +public: + explicit HLE_BindShader(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + auto& regs = maxwell3d.regs; + const u32 index = parameters[0]; + if ((parameters[1] - regs.shadow_scratch[28 + index]) == 0) { + return; + } + + regs.pipelines[index & 0xF].offset = parameters[2]; + maxwell3d.dirty.flags[VideoCommon::Dirty::Shaders] = true; + regs.shadow_scratch[28 + index] = parameters[1]; + regs.shadow_scratch[34 + index] = parameters[2]; + + const u32 address = parameters[4]; + auto& const_buffer = regs.const_buffer; + const_buffer.size = 0x10000; + const_buffer.address_high = (address >> 24) & 0xFF; + const_buffer.address_low = address << 8; + + const size_t bind_group_id = parameters[3] & 0x7F; + auto& bind_group = regs.bind_groups[bind_group_id]; + bind_group.raw_config = 0x11; + maxwell3d.ProcessCBBind(bind_group_id); + } +}; + +class HLE_SetRasterBoundingBox final : public HLEMacroImpl { +public: + explicit HLE_SetRasterBoundingBox(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + const u32 raster_mode = parameters[0]; + auto& regs = maxwell3d.regs; + const u32 raster_enabled = maxwell3d.regs.conservative_raster_enable; + const u32 scratch_data = maxwell3d.regs.shadow_scratch[52]; + regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F; + regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled); + } +}; + +template <size_t base_size> +class HLE_ClearConstBuffer final : public HLEMacroImpl { +public: + explicit HLE_ClearConstBuffer(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + static constexpr std::array<u32, base_size> zeroes{}; + auto& regs = maxwell3d.regs; + regs.const_buffer.size = static_cast<u32>(base_size); + regs.const_buffer.address_high = parameters[0]; + regs.const_buffer.address_low = parameters[1]; + regs.const_buffer.offset = 0; + maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4); + } +}; + +class HLE_ClearMemory final : public HLEMacroImpl { public: - explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) - : maxwell3d{maxwell3d_}, func{func_} {} + explicit HLE_ClearMemory(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); - void Execute(const std::vector<u32>& parameters, u32 method) override { - func(maxwell3d, parameters); + const u32 needed_memory = parameters[2] / sizeof(u32); + if (needed_memory > zero_memory.size()) { + zero_memory.resize(needed_memory, 0); + } + auto& regs = maxwell3d.regs; + regs.upload.line_length_in = parameters[2]; + regs.upload.line_count = 1; + regs.upload.dest.address_high = parameters[0]; + regs.upload.dest.address_low = parameters[1]; + maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); + maxwell3d.CallMultiMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)), + zero_memory.data(), needed_memory, needed_memory); } private: - Engines::Maxwell3D& maxwell3d; - HLEFunction func; + std::vector<u32> zero_memory; +}; + +class HLE_TransformFeedbackSetup final : public HLEMacroImpl { +public: + explicit HLE_TransformFeedbackSetup(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} + + void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { + maxwell3d.RefreshParameters(); + + auto& regs = maxwell3d.regs; + regs.transform_feedback_enabled = 1; + regs.transform_feedback.buffers[0].start_offset = 0; + regs.transform_feedback.buffers[1].start_offset = 0; + regs.transform_feedback.buffers[2].start_offset = 0; + regs.transform_feedback.buffers[3].start_offset = 0; + + regs.upload.line_length_in = 4; + regs.upload.line_count = 1; + regs.upload.dest.address_high = parameters[0]; + regs.upload.dest.address_low = parameters[1]; + maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); + maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)), + regs.transform_feedback.controls[0].stride, true); + } }; } // Anonymous namespace -HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} +HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} { + builders.emplace(0x0D61FC9FAAC9FCADULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_DrawArraysIndirect<false>>(maxwell3d__); + })); + builders.emplace(0x8A4D173EB99A8603ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_DrawArraysIndirect<true>>(maxwell3d__); + })); + builders.emplace(0x771BB18C62444DA0ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_DrawIndexedIndirect<false>>(maxwell3d__); + })); + builders.emplace(0x0217920100488FF7ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_DrawIndexedIndirect<true>>(maxwell3d__); + })); + builders.emplace(0x3F5E74B9C9A50164ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_MultiDrawIndexedIndirectCount>( + maxwell3d__); + })); + builders.emplace(0xEAD26C3E2109B06BULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_MultiLayerClear>(maxwell3d__); + })); + builders.emplace(0xC713C83D8F63CCF3ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_C713C83D8F63CCF3>(maxwell3d__); + })); + builders.emplace(0xD7333D26E0A93EDEULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_D7333D26E0A93EDE>(maxwell3d__); + })); + builders.emplace(0xEB29B2A09AA06D38ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_BindShader>(maxwell3d__); + })); + builders.emplace(0xDB1341DBEB4C8AF7ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_SetRasterBoundingBox>(maxwell3d__); + })); + builders.emplace(0x6C97861D891EDf7EULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_ClearConstBuffer<0x5F00>>(maxwell3d__); + })); + builders.emplace(0xD246FDDF3A6173D7ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_ClearConstBuffer<0x7000>>(maxwell3d__); + })); + builders.emplace(0xEE4D0004BEC8ECF4ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_ClearMemory>(maxwell3d__); + })); + builders.emplace(0xFC0CF27F5FFAA661ULL, + std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( + [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { + return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__); + })); +} + HLEMacro::~HLEMacro() = default; std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { - const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), - [hash](const auto& pair) { return pair.first == hash; }); - if (it == hle_funcs.end()) { + const auto it = builders.find(hash); + if (it == builders.end()) { return nullptr; } - return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); + return it->second(maxwell3d); } } // namespace Tegra diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h index 625332c9d..33f92fab1 100644 --- a/src/video_core/macro/macro_hle.h +++ b/src/video_core/macro/macro_hle.h @@ -3,7 +3,10 @@ #pragma once +#include <functional> #include <memory> +#include <unordered_map> + #include "common/common_types.h" namespace Tegra { @@ -23,6 +26,8 @@ public: private: Engines::Maxwell3D& maxwell3d; + std::unordered_map<u64, std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>> + builders; }; } // namespace Tegra diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 8c8dfcca6..83924475b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -11,6 +11,7 @@ #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/memory.h" +#include "video_core/invalidation_accumulator.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" @@ -25,7 +26,9 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits != big_page_bits ? page_bits : 0}, - unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { + kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add( + 1, std::memory_order_acq_rel)}, + accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} { address_space_size = 1ULL << address_space_bits; page_size = 1ULL << page_bits; page_mask = page_size - 1ULL; @@ -41,11 +44,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_entries.resize(big_page_table_size / 32, 0); big_page_table_cpu.resize(big_page_table_size); big_page_continous.resize(big_page_table_size / continous_bits, 0); - std::array<PTEKind, 32> kind_valus; - kind_valus.fill(PTEKind::INVALID); - big_kinds.resize(big_page_table_size / 32, kind_valus); entries.resize(page_table_size / 32, 0); - kinds.resize(page_table_size / 32, kind_valus); } MemoryManager::~MemoryManager() = default; @@ -83,38 +82,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { } PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { - auto entry = GetEntry<true>(gpu_addr); - if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] { - return GetKind<true>(gpu_addr); - } else { - return GetKind<false>(gpu_addr); - } -} - -template <bool is_big_page> -PTEKind MemoryManager::GetKind(size_t position) const { - if constexpr (is_big_page) { - position = position >> big_page_bits; - const size_t sub_index = position % 32; - return big_kinds[position / 32][sub_index]; - } else { - position = position >> page_bits; - const size_t sub_index = position % 32; - return kinds[position / 32][sub_index]; - } -} - -template <bool is_big_page> -void MemoryManager::SetKind(size_t position, PTEKind kind) { - if constexpr (is_big_page) { - position = position >> big_page_bits; - const size_t sub_index = position % 32; - big_kinds[position / 32][sub_index] = kind; - } else { - position = position >> page_bits; - const size_t sub_index = position % 32; - kinds[position / 32][sub_index] = kind; - } + return kind_map.GetValueAt(gpu_addr); } inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { @@ -141,7 +109,6 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp const GPUVAddr current_gpu_addr = gpu_addr + offset; [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); SetEntry<false>(current_gpu_addr, entry_type); - SetKind<false>(current_gpu_addr, kind); if (current_entry_type != entry_type) { rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); } @@ -153,6 +120,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp } remaining_size -= page_size; } + kind_map.Map(gpu_addr, gpu_addr + size, kind); return gpu_addr; } @@ -164,7 +132,6 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr const GPUVAddr current_gpu_addr = gpu_addr + offset; [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); SetEntry<true>(current_gpu_addr, entry_type); - SetKind<true>(current_gpu_addr, kind); if (current_entry_type != entry_type) { rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); } @@ -193,6 +160,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr } remaining_size -= big_page_size; } + kind_map.Map(gpu_addr, gpu_addr + size, kind); return gpu_addr; } @@ -219,15 +187,12 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { if (size == 0) { return; } - const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); - - for (const auto& [map_addr, map_size] : submapped_ranges) { - // Flush and invalidate through the GPU interface, to be asynchronous if possible. - const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr); - ASSERT(cpu_addr); + GetSubmappedRangeImpl<false>(gpu_addr, size, page_stash); - rasterizer->UnmapMemory(*cpu_addr, map_size); + for (const auto& [map_addr, map_size] : page_stash) { + rasterizer->UnmapMemory(map_addr, map_size); } + page_stash.clear(); BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); @@ -325,9 +290,15 @@ template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typenam inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const { - static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>; - static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>; - static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>; + using FuncMappedReturn = + typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type; + using FuncReservedReturn = + typename std::invoke_result<FuncReserved, std::size_t, std::size_t, std::size_t>::type; + using FuncUnmappedReturn = + typename std::invoke_result<FuncUnmapped, std::size_t, std::size_t, std::size_t>::type; + static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMappedReturn, bool>; + static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReservedReturn, bool>; + static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmappedReturn, bool>; u64 used_page_size; u64 used_page_mask; u64 used_page_bits; @@ -384,8 +355,8 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si } template <bool is_safe> -void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, - std::size_t size) const { +void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, + [[maybe_unused]] VideoCommon::CacheType which) const { auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { std::memset(dest_buffer, 0, copy_amount); @@ -395,7 +366,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, const VAddr cpu_addr_base = (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; if constexpr (is_safe) { - rasterizer->FlushRegion(cpu_addr_base, copy_amount); + rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); } u8* physical = memory.GetPointer(cpu_addr_base); std::memcpy(dest_buffer, physical, copy_amount); @@ -405,7 +376,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, const VAddr cpu_addr_base = (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; if constexpr (is_safe) { - rasterizer->FlushRegion(cpu_addr_base, copy_amount); + rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); } if (!IsBigPageContinous(page_index)) [[unlikely]] { memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); @@ -423,18 +394,19 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); } -void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { - ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size); +void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, + VideoCommon::CacheType which) const { + ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which); } void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const { - ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size); + ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); } template <bool is_safe> -void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, - std::size_t size) { +void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, + [[maybe_unused]] VideoCommon::CacheType which) { auto just_advance = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; @@ -443,7 +415,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe const VAddr cpu_addr_base = (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; if constexpr (is_safe) { - rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which); } u8* physical = memory.GetPointer(cpu_addr_base); std::memcpy(physical, src_buffer, copy_amount); @@ -453,7 +425,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe const VAddr cpu_addr_base = (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; if constexpr (is_safe) { - rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which); } if (!IsBigPageContinous(page_index)) [[unlikely]] { memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); @@ -471,16 +443,24 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); } -void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size); +void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, + VideoCommon::CacheType which) { + WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which); } void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size); + WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None); +} + +void MemoryManager::WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, + std::size_t size) { + WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None); + accumulator->Add(gpu_dest_addr, size); } -void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { +void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which) const { auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t copy_amount) {}; @@ -488,12 +468,12 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; - rasterizer->FlushRegion(cpu_addr_base, copy_amount); + rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); }; auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; - rasterizer->FlushRegion(cpu_addr_base, copy_amount); + rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); }; auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { @@ -503,7 +483,8 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); } -bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { +bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which) const { bool result = false; auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, @@ -512,13 +493,13 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; - result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which); return result; }; auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; - result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which); return result; }; auto check_short_pages = [&](std::size_t page_index, std::size_t offset, @@ -571,7 +552,12 @@ size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const { return range_so_far; } -void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { +size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { + return kind_map.GetContinousSizeFrom(gpu_addr); +} + +void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which) const { auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t copy_amount) {}; @@ -579,12 +565,12 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; - rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which); }; auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { const VAddr cpu_addr_base = (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; - rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which); }; auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { @@ -594,14 +580,15 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); } -void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { +void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, + VideoCommon::CacheType which) { std::vector<u8> tmp_buffer(size); - ReadBlock(gpu_src_addr, tmp_buffer.data(), size); + ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); // The output block must be flushed in case it has data modified from the GPU. // Fixes NPC geometry in Zombie Panic in Wonderland DX - FlushRegion(gpu_dest_addr, size); - WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); + FlushRegion(gpu_dest_addr, size, which); + WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which); } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { @@ -681,7 +668,17 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( GPUVAddr gpu_addr, std::size_t size) const { std::vector<std::pair<GPUVAddr, std::size_t>> result{}; - std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{}; + GetSubmappedRangeImpl<true>(gpu_addr, size, result); + return result; +} + +template <bool is_gpu_address> +void MemoryManager::GetSubmappedRangeImpl( + GPUVAddr gpu_addr, std::size_t size, + std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& + result) const { + std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>> + last_segment{}; std::optional<VAddr> old_page_addr{}; const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, @@ -703,8 +700,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( } old_page_addr = {cpu_addr_base + copy_amount}; if (!last_segment) { - const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; - last_segment = {new_base_addr, copy_amount}; + if constexpr (is_gpu_address) { + const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment = {cpu_addr_base, copy_amount}; + } } else { last_segment->second += copy_amount; } @@ -721,8 +722,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( } old_page_addr = {cpu_addr_base + copy_amount}; if (!last_segment) { - const GPUVAddr new_base_addr = (page_index << page_bits) + offset; - last_segment = {new_base_addr, copy_amount}; + if constexpr (is_gpu_address) { + const GPUVAddr new_base_addr = (page_index << page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment = {cpu_addr_base, copy_amount}; + } } else { last_segment->second += copy_amount; } @@ -733,7 +738,18 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( }; MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages); split(0, 0, 0); - return result; +} + +void MemoryManager::FlushCaching() { + if (!accumulator->AnyAccumulated()) { + return; + } + accumulator->Callback([this](GPUVAddr addr, size_t size) { + GetSubmappedRangeImpl<false>(addr, size, page_stash); + }); + rasterizer->InnerInvalidation(page_stash); + page_stash.clear(); + accumulator->Clear(); } } // namespace Tegra diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index ab4bc9ec6..9ebfb6179 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -10,13 +10,19 @@ #include "common/common_types.h" #include "common/multi_level_page_table.h" +#include "common/range_map.h" #include "common/virtual_buffer.h" +#include "video_core/cache_types.h" #include "video_core/pte_kind.h" namespace VideoCore { class RasterizerInterface; } +namespace VideoCommon { +class InvalidationAccumulator; +} + namespace Core { class DeviceMemory; namespace Memory { @@ -59,9 +65,12 @@ public: * in the Host Memory counterpart. Note: This functions cause Host GPU Memory * Flushes and Invalidations, respectively to each operation. */ - void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; - void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); - void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); + void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) const; + void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All); + void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All); /** * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and @@ -75,6 +84,7 @@ public: */ void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + void WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); /** * Checks if a gpu region can be simply read with a pointer. @@ -104,11 +114,14 @@ public: GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); void Unmap(GPUVAddr gpu_addr, std::size_t size); - void FlushRegion(GPUVAddr gpu_addr, size_t size) const; + void FlushRegion(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) const; - void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; + void InvalidateRegion(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) const; - bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; + bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) const; size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; @@ -118,16 +131,23 @@ public: PTEKind GetPageKind(GPUVAddr gpu_addr) const; + size_t GetMemoryLayoutSize(GPUVAddr gpu_addr, + size_t max_size = std::numeric_limits<size_t>::max()) const; + + void FlushCaching(); + private: template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; template <bool is_safe> - void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; + void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, + VideoCommon::CacheType which) const; template <bool is_safe> - void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, + VideoCommon::CacheType which); template <bool is_big_page> [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { @@ -141,6 +161,12 @@ private: inline bool IsBigPageContinous(size_t big_page_index) const; inline void SetBigPageContinous(size_t big_page_index, bool value); + template <bool is_gpu_address> + void GetSubmappedRangeImpl( + GPUVAddr gpu_addr, std::size_t size, + std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& + result) const; + Core::System& system; Core::Memory::Memory& memory; Core::DeviceMemory& device_memory; @@ -183,23 +209,17 @@ private: template <bool is_big_page> inline void SetEntry(size_t position, EntryType entry); - std::vector<std::array<PTEKind, 32>> kinds; - std::vector<std::array<PTEKind, 32>> big_kinds; - - template <bool is_big_page> - inline PTEKind GetKind(size_t position) const; - - template <bool is_big_page> - inline void SetKind(size_t position, PTEKind kind); - Common::MultiLevelPageTable<u32> page_table; + Common::RangeMap<GPUVAddr, PTEKind> kind_map; Common::VirtualBuffer<u32> big_page_table_cpu; std::vector<u64> big_page_continous; + std::vector<std::pair<VAddr, std::size_t>> page_stash{}; constexpr static size_t continous_bits = 64; const size_t unique_identifier; + std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator; static std::atomic<size_t> unique_identifier_generator; }; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index b6907463c..33e2610bc 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -6,8 +6,10 @@ #include <functional> #include <optional> #include <span> +#include <utility> #include "common/common_types.h" #include "common/polyfill_thread.h" +#include "video_core/cache_types.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" @@ -42,6 +44,12 @@ public: /// Dispatches a draw invocation virtual void Draw(bool is_indexed, u32 instance_count) = 0; + /// Dispatches an indirect draw invocation + virtual void DrawIndirect() {} + + /// Dispatches an draw texture invocation + virtual void DrawTexture() = 0; + /// Clear the current framebuffer virtual void Clear(u32 layer_count) = 0; @@ -80,13 +88,22 @@ public: virtual void FlushAll() = 0; /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory - virtual void FlushRegion(VAddr addr, u64 size) = 0; + virtual void FlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; /// Check if the the specified memory area requires flushing to CPU Memory. - virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; + virtual bool MustFlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; /// Notify rasterizer that any caches of the specified region should be invalidated - virtual void InvalidateRegion(VAddr addr, u64 size) = 0; + virtual void InvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; + + virtual void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) { + for (const auto& [cpu_addr, size] : sequences) { + InvalidateRegion(cpu_addr, size); + } + } /// Notify rasterizer that any caches of the specified region are desync with guest virtual void OnCPUWrite(VAddr addr, u64 size) = 0; @@ -102,7 +119,8 @@ public: /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// and invalidated - virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; + virtual void FlushAndInvalidateRegion( + VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; /// Notify the host renderer to wait for previous primitive and compute operations. virtual void WaitForIdle() = 0; @@ -119,6 +137,10 @@ public: /// Notify rasterizer that a frame is about to finish virtual void TickFrame() = 0; + virtual bool AccelerateConditionalRendering() { + return false; + } + /// Attempt to use a faster method to perform a surface copy [[nodiscard]] virtual bool AccelerateSurfaceCopy( const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index 9734d84bc..2b5c7defa 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -21,6 +21,7 @@ RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gp RasterizerNull::~RasterizerNull() = default; void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} +void RasterizerNull::DrawTexture() {} void RasterizerNull::Clear(u32 layer_count) {} void RasterizerNull::DispatchCompute() {} void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} @@ -39,11 +40,11 @@ void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr u32 size) {} void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} void RasterizerNull::FlushAll() {} -void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} -bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { +void RasterizerNull::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} +bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) { return false; } -void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} void RasterizerNull::InvalidateGPUCache() {} void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} @@ -61,7 +62,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) { } void RasterizerNull::SignalReference() {} void RasterizerNull::ReleaseFences() {} -void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} void RasterizerNull::WaitForIdle() {} void RasterizerNull::FragmentBarrier() {} void RasterizerNull::TiledCacheBarrier() {} diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index ecf77ba42..51f896e43 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h @@ -31,6 +31,7 @@ public: ~RasterizerNull() override; void Draw(bool is_indexed, u32 instance_count) override; + void DrawTexture() override; void Clear(u32 layer_count) override; void DispatchCompute() override; void ResetCounter(VideoCore::QueryType type) override; @@ -38,9 +39,12 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void FlushAll() override; - void FlushRegion(VAddr addr, u64 size) override; - bool MustFlushRegion(VAddr addr, u64 size) override; - void InvalidateRegion(VAddr addr, u64 size) override; + void FlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + bool MustFlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void InvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void OnCPUWrite(VAddr addr, u64 size) override; void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; @@ -50,7 +54,8 @@ public: void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; - void FlushAndInvalidateRegion(VAddr addr, u64 size) override; + void FlushAndInvalidateRegion( + VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void WaitForIdle() override; void FragmentBarrier() override; void TiledCacheBarrier() override; diff --git a/src/video_core/renderer_opengl/blit_image.cpp b/src/video_core/renderer_opengl/blit_image.cpp new file mode 100644 index 000000000..9a560a73b --- /dev/null +++ b/src/video_core/renderer_opengl/blit_image.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> + +#include "video_core/host_shaders/blit_color_float_frag.h" +#include "video_core/host_shaders/full_screen_triangle_vert.h" +#include "video_core/renderer_opengl/blit_image.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" + +namespace OpenGL { + +BlitImageHelper::BlitImageHelper(ProgramManager& program_manager_) + : program_manager(program_manager_), + full_screen_vert(CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER)), + blit_color_to_color_frag( + CreateProgram(HostShaders::BLIT_COLOR_FLOAT_FRAG, GL_FRAGMENT_SHADER)) {} + +BlitImageHelper::~BlitImageHelper() = default; + +void BlitImageHelper::BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler, + const Region2D& dst_region, const Region2D& src_region, + const Extent3D& src_size) { + glEnable(GL_CULL_FACE); + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_RASTERIZER_DISCARD); + glDisable(GL_ALPHA_TEST); + glDisablei(GL_BLEND, 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthRangeIndexed(0, 0.0, 0.0); + + program_manager.BindPresentPrograms(full_screen_vert.handle, blit_color_to_color_frag.handle); + glProgramUniform2f(full_screen_vert.handle, 0, + static_cast<float>(src_region.end.x - src_region.start.x) / + static_cast<float>(src_size.width), + static_cast<float>(src_region.end.y - src_region.start.y) / + static_cast<float>(src_size.height)); + glProgramUniform2f(full_screen_vert.handle, 1, + static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width), + static_cast<float>(src_region.start.y) / + static_cast<float>(src_size.height)); + glViewport(std::min(dst_region.start.x, dst_region.end.x), + std::min(dst_region.start.y, dst_region.end.y), + std::abs(dst_region.end.x - dst_region.start.x), + std::abs(dst_region.end.y - dst_region.start.y)); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer); + glBindSampler(0, src_sampler); + glBindTextureUnit(0, src_image_view); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 3); +} +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/blit_image.h b/src/video_core/renderer_opengl/blit_image.h new file mode 100644 index 000000000..5a2b12d16 --- /dev/null +++ b/src/video_core/renderer_opengl/blit_image.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <glad/glad.h> + +#include "video_core/engines/fermi_2d.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/texture_cache/types.h" + +namespace OpenGL { + +using VideoCommon::Extent3D; +using VideoCommon::Offset2D; +using VideoCommon::Region2D; + +class ProgramManager; +class Framebuffer; +class ImageView; + +class BlitImageHelper { +public: + explicit BlitImageHelper(ProgramManager& program_manager); + ~BlitImageHelper(); + + void BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler, + const Region2D& dst_region, const Region2D& src_region, + const Extent3D& src_size); + +private: + ProgramManager& program_manager; + + OGLProgram full_screen_vert; + OGLProgram blit_color_to_color_frag; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index a8c3f8b67..bb1962073 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -160,6 +160,10 @@ public: return device.CanReportMemoryUsage(); } + u32 GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetShaderStorageBufferAlignment()); + } + private: static constexpr std::array PABO_LUT{ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index cee5c3247..22ed16ebf 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -166,6 +166,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64"); has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float; has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; + has_draw_texture = GLAD_GL_NV_draw_texture; warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; need_fastmath_off = is_nvidia; can_report_memory = GLAD_GL_NVX_gpu_memory_info; diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 2a72d84be..3ff8cad83 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -4,6 +4,8 @@ #pragma once #include <cstddef> +#include <string> + #include "common/common_types.h" #include "core/frontend/emu_window.h" #include "shader_recompiler/stage.h" @@ -146,6 +148,10 @@ public: return has_sparse_texture_2; } + bool HasDrawTexture() const { + return has_draw_texture; + } + bool IsWarpSizePotentiallyLargerThanGuest() const { return warp_size_potentially_larger_than_guest; } @@ -216,6 +222,7 @@ private: bool has_shader_int64{}; bool has_amd_shader_half_float{}; bool has_sparse_texture_2{}; + bool has_draw_texture{}; bool warp_size_potentially_larger_than_guest{}; bool need_fastmath_off{}; bool has_cbuf_ftou_bug{}; diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp new file mode 100644 index 000000000..77262dcf1 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_fsr.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "video_core/fsr.h" +#include "video_core/renderer_opengl/gl_fsr.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" + +namespace OpenGL { +using namespace FSR; + +using FsrConstants = std::array<u32, 4 * 4>; + +FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, + std::string_view fsr_rcas_source) + : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)}, + fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)}, + fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} { + glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f); + glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f); +} + +FSR::~FSR() = default; + +void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, + u32 input_image_width, u32 input_image_height, + const Common::Rectangle<int>& crop_rect) { + + const auto output_image_width = screen.GetWidth(); + const auto output_image_height = screen.GetHeight(); + + if (fsr_intermediate_tex.handle) { + GLint fsr_tex_width, fsr_tex_height; + glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH, + &fsr_tex_width); + glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT, + &fsr_tex_height); + if (static_cast<u32>(fsr_tex_width) != output_image_width || + static_cast<u32>(fsr_tex_height) != output_image_height) { + fsr_intermediate_tex.Release(); + } + } + if (!fsr_intermediate_tex.handle) { + fsr_intermediate_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width, + output_image_height); + glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0, + fsr_intermediate_tex.handle, 0); + } + + GLint old_draw_fb; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + + glFrontFace(GL_CW); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width), + static_cast<GLfloat>(output_image_height)); + + FsrConstants constants; + FsrEasuConOffset( + constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12, + + static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), + static_cast<f32>(input_image_width), static_cast<f32>(input_image_height), + static_cast<f32>(output_image_width), static_cast<f32>(output_image_height), + static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top)); + + glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants)); + + program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); + glBindTextureUnit(0, fsr_intermediate_tex.handle); + + const float sharpening = + static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + + FsrRcasCon(constants.data(), sharpening); + glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants)); +} + +void FSR::InitBuffers() { + fsr_framebuffer.Create(); +} + +void FSR::ReleaseBuffers() { + fsr_framebuffer.Release(); + fsr_intermediate_tex.Release(); +} + +const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept { + return fsr_rcas_frag; +} + +bool FSR::AreBuffersInitialized() const noexcept { + return fsr_framebuffer.handle; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h new file mode 100644 index 000000000..1f6ae3115 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_fsr.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string_view> + +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/fsr.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +class ProgramManager; + +class FSR { +public: + explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, + std::string_view fsr_rcas_source); + ~FSR(); + + void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, + u32 input_image_width, u32 input_image_height, + const Common::Rectangle<int>& crop_rect); + + void InitBuffers(); + + void ReleaseBuffers(); + + [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept; + + [[nodiscard]] bool AreBuffersInitialized() const noexcept; + +private: + OGLFramebuffer fsr_framebuffer; + OGLProgram fsr_vertex; + OGLProgram fsr_easu_frag; + OGLProgram fsr_rcas_frag; + OGLTexture fsr_intermediate_tex; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index ea53ddb46..1c06b3655 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -40,6 +40,7 @@ struct GraphicsPipelineKey { BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; BitField<10, 1, u32> tessellation_clockwise; + BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; }; std::array<u32, 3> padding; VideoCommon::TransformFeedbackState xfb_state; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a44b8c454..7bced675c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -64,7 +64,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, state_tracker, gpu.ShaderNotify()), query_cache(*this), accelerate_dma(buffer_cache), - fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} + fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), + blit_image(program_manager_) {} RasterizerOpenGL::~RasterizerOpenGL() = default; @@ -139,6 +140,7 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load void RasterizerOpenGL::Clear(u32 layer_count) { MICROPROFILE_SCOPE(OpenGL_Clears); + gpu_memory->FlushCaching(); const auto& regs = maxwell3d->regs; bool use_color{}; bool use_depth{}; @@ -202,10 +204,12 @@ void RasterizerOpenGL::Clear(u32 layer_count) { ++num_queued_commands; } -void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { +template <typename Func> +void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { MICROPROFILE_SCOPE(OpenGL_Drawing); SCOPE_EXIT({ gpu.TickWork(); }); + gpu_memory->FlushCaching(); query_cache.UpdateCounters(); GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; @@ -226,49 +230,140 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology); BeginTransformFeedback(pipeline, primitive_mode); - const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance); - const GLsizei num_instances = static_cast<GLsizei>(instance_count); - if (is_indexed) { - const GLint base_vertex = static_cast<GLint>(draw_state.base_index); - const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count); - const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); - const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format); - if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { - glDrawElements(primitive_mode, num_vertices, format, offset); - } else if (num_instances == 1 && base_instance == 0) { - glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex); - } else if (base_vertex == 0 && base_instance == 0) { - glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances); - } else if (base_vertex == 0) { - glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset, - num_instances, base_instance); - } else if (base_instance == 0) { - glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset, - num_instances, base_vertex); + draw_func(primitive_mode); + + EndTransformFeedback(); + + ++num_queued_commands; + has_written_global_memory |= pipeline->WritesGlobalMemory(); +} + +void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { + PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) { + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance); + const GLsizei num_instances = static_cast<GLsizei>(instance_count); + if (is_indexed) { + const GLint base_vertex = static_cast<GLint>(draw_state.base_index); + const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count); + const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); + const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format); + if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { + glDrawElements(primitive_mode, num_vertices, format, offset); + } else if (num_instances == 1 && base_instance == 0) { + glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex); + } else if (base_vertex == 0 && base_instance == 0) { + glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, + num_instances); + } else if (base_vertex == 0) { + glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset, + num_instances, base_instance); + } else if (base_instance == 0) { + glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset, + num_instances, base_vertex); + } else { + glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format, + offset, num_instances, base_vertex, + base_instance); + } } else { - glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format, - offset, num_instances, base_vertex, - base_instance); + const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first); + const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count); + if (num_instances == 1 && base_instance == 0) { + glDrawArrays(primitive_mode, base_vertex, num_vertices); + } else if (base_instance == 0) { + glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances); + } else { + glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, + num_instances, base_instance); + } } - } else { - const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first); - const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count); - if (num_instances == 1 && base_instance == 0) { - glDrawArrays(primitive_mode, base_vertex, num_vertices); - } else if (base_instance == 0) { - glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances); + }); +} + +void RasterizerOpenGL::DrawIndirect() { + const auto& params = maxwell3d->draw_manager->GetIndirectParams(); + buffer_cache.SetDrawIndirect(¶ms); + PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) { + const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer(); + const GLvoid* const gl_offset = + reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset)); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->Handle()); + if (params.include_count) { + const auto [draw_buffer, offset_base] = buffer_cache.GetDrawIndirectCount(); + glBindBuffer(GL_PARAMETER_BUFFER, draw_buffer->Handle()); + + if (params.is_indexed) { + const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format); + glMultiDrawElementsIndirectCount(primitive_mode, format, gl_offset, + static_cast<GLintptr>(offset_base), + static_cast<GLsizei>(params.max_draw_counts), + static_cast<GLsizei>(params.stride)); + } else { + glMultiDrawArraysIndirectCount(primitive_mode, gl_offset, + static_cast<GLintptr>(offset_base), + static_cast<GLsizei>(params.max_draw_counts), + static_cast<GLsizei>(params.stride)); + } + return; + } + if (params.is_indexed) { + const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format); + glMultiDrawElementsIndirect(primitive_mode, format, gl_offset, + static_cast<GLsizei>(params.max_draw_counts), + static_cast<GLsizei>(params.stride)); } else { - glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, - num_instances, base_instance); + glMultiDrawArraysIndirect(primitive_mode, gl_offset, + static_cast<GLsizei>(params.max_draw_counts), + static_cast<GLsizei>(params.stride)); } + }); + buffer_cache.SetDrawIndirect(nullptr); +} + +void RasterizerOpenGL::DrawTexture() { + MICROPROFILE_SCOPE(OpenGL_Drawing); + + SCOPE_EXIT({ gpu.TickWork(); }); + query_cache.UpdateCounters(); + + texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.UpdateRenderTargets(false); + + SyncState(); + + const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); + const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); + const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); + + if (device.HasDrawTexture()) { + state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); + + glDrawTextureNV(texture.DefaultHandle(), sampler->Handle(), draw_texture_state.dst_x0, + draw_texture_state.dst_y0, draw_texture_state.dst_x1, + draw_texture_state.dst_y1, 0, + draw_texture_state.src_x0 / static_cast<float>(texture.size.width), + draw_texture_state.src_y0 / static_cast<float>(texture.size.height), + draw_texture_state.src_x1 / static_cast<float>(texture.size.width), + draw_texture_state.src_y1 / static_cast<float>(texture.size.height)); + } else { + Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0), + .y = static_cast<s32>(draw_texture_state.dst_y0)}, + Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1), + .y = static_cast<s32>(draw_texture_state.dst_y1)}}; + Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0), + .y = static_cast<s32>(draw_texture_state.src_y0)}, + Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1), + .y = static_cast<s32>(draw_texture_state.src_y1)}}; + blit_image.BlitColor(texture_cache.GetFramebuffer()->Handle(), texture.DefaultHandle(), + sampler->Handle(), dst_region, src_region, texture.size); } - EndTransformFeedback(); ++num_queued_commands; - has_written_global_memory |= pipeline->WritesGlobalMemory(); } void RasterizerOpenGL::DispatchCompute() { + gpu_memory->FlushCaching(); ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; if (!pipeline) { return; @@ -302,46 +397,60 @@ void RasterizerOpenGL::DisableGraphicsUniformBuffer(size_t stage, u32 index) { void RasterizerOpenGL::FlushAll() {} -void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { +void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); if (addr == 0 || size == 0) { return; } - { + if (True(which & VideoCommon::CacheType::TextureCache)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.DownloadMemory(addr, size); } - { + if ((True(which & VideoCommon::CacheType::BufferCache))) { std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.DownloadMemory(addr, size); } - query_cache.FlushRegion(addr, size); + if ((True(which & VideoCommon::CacheType::QueryCache))) { + query_cache.FlushRegion(addr, size); + } } -bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; +bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { + if ((True(which & VideoCommon::CacheType::BufferCache))) { + std::scoped_lock lock{buffer_cache.mutex}; + if (buffer_cache.IsRegionGpuModified(addr, size)) { + return true; + } + } if (!Settings::IsGPULevelHigh()) { - return buffer_cache.IsRegionGpuModified(addr, size); + return false; + } + if (True(which & VideoCommon::CacheType::TextureCache)) { + std::scoped_lock lock{texture_cache.mutex}; + return texture_cache.IsRegionGpuModified(addr, size); } - return texture_cache.IsRegionGpuModified(addr, size) || - buffer_cache.IsRegionGpuModified(addr, size); + return false; } -void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { +void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); if (addr == 0 || size == 0) { return; } - { + if (True(which & VideoCommon::CacheType::TextureCache)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(addr, size); } - { + if (True(which & VideoCommon::CacheType::BufferCache)) { std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.WriteMemory(addr, size); } - shader_cache.InvalidateRegion(addr, size); - query_cache.InvalidateRegion(addr, size); + if (True(which & VideoCommon::CacheType::ShaderCache)) { + shader_cache.InvalidateRegion(addr, size); + } + if (True(which & VideoCommon::CacheType::QueryCache)) { + query_cache.InvalidateRegion(addr, size); + } } void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { @@ -408,11 +517,12 @@ void RasterizerOpenGL::ReleaseFences() { fence_manager.WaitPendingFences(); } -void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { +void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which) { if (Settings::IsGPULevelExtreme()) { - FlushRegion(addr, size); + FlushRegion(addr, size, which); } - InvalidateRegion(addr, size); + InvalidateRegion(addr, size, which); } void RasterizerOpenGL::WaitForIdle() { @@ -460,6 +570,22 @@ void RasterizerOpenGL::TickFrame() { } } +bool RasterizerOpenGL::AccelerateConditionalRendering() { + gpu_memory->FlushCaching(); + if (Settings::IsGPULevelHigh()) { + // Reimplement Host conditional rendering. + return false; + } + // Medium / Low Hack: stub any checks on queries writen into the buffer cache. + const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()}; + Maxwell::ReportSemaphore::Compare cmp; + if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp), + VideoCommon::CacheType::BufferCache)) { + return true; + } + return false; +} + bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { @@ -481,7 +607,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::unique_lock<std::mutex> lock{buffer_cache.mutex}; + std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fc183c3ca..0c45832ae 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -16,6 +16,7 @@ #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" +#include "video_core/renderer_opengl/blit_image.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_fence_manager.h" @@ -69,6 +70,8 @@ public: ~RasterizerOpenGL() override; void Draw(bool is_indexed, u32 instance_count) override; + void DrawIndirect() override; + void DrawTexture() override; void Clear(u32 layer_count) override; void DispatchCompute() override; void ResetCounter(VideoCore::QueryType type) override; @@ -76,9 +79,12 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void FlushAll() override; - void FlushRegion(VAddr addr, u64 size) override; - bool MustFlushRegion(VAddr addr, u64 size) override; - void InvalidateRegion(VAddr addr, u64 size) override; + void FlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + bool MustFlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void InvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void OnCPUWrite(VAddr addr, u64 size) override; void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; @@ -88,12 +94,14 @@ public: void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; - void FlushAndInvalidateRegion(VAddr addr, u64 size) override; + void FlushAndInvalidateRegion( + VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void WaitForIdle() override; void FragmentBarrier() override; void TiledCacheBarrier() override; void FlushCommands() override; void TickFrame() override; + bool AccelerateConditionalRendering() override; bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; @@ -121,6 +129,9 @@ private: static constexpr size_t MAX_IMAGES = 48; static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES; + template <typename Func> + void PrepareDraw(bool is_indexed, Func&&); + /// Syncs state to match guest's void SyncState(); @@ -215,6 +226,8 @@ private: AccelerateDMA accelerate_dma; FenceManagerOpenGL fence_manager; + BlitImageHelper blit_image; + boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices; std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids; boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index f8868a012..7dd854e0f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 7; +constexpr u32 CACHE_VERSION = 9; template <typename Container> auto MakeSpan(Container& container) { @@ -236,6 +236,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, .support_viewport_index_layer = device.HasVertexViewportLayer(), + .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), + .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), } { if (use_asynchronous_shaders) { workers = CreateWorkers(); @@ -350,6 +352,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { regs.tessellation.params.output_primitives.Value() == Maxwell::Tessellation::OutputPrimitives::Triangles_CW); graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); + graphics_key.app_stage.Assign(maxwell3d->engine_state); if (graphics_key.xfb_enabled) { SetXfbState(graphics_key.xfb_state, regs); } diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index d9c29d8b7..98841ae65 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -1,2 +1,123 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later + +#include <glad/glad.h> + +#include "video_core/renderer_opengl/gl_shader_manager.h" + +namespace OpenGL { + +static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{ + GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV, + GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, +}; + +ProgramManager::ProgramManager(const Device& device) { + glCreateProgramPipelines(1, &pipeline.handle); + if (device.UseAssemblyShaders()) { + glEnable(GL_COMPUTE_PROGRAM_NV); + } +} + +void ProgramManager::BindComputeProgram(GLuint program) { + glUseProgram(program); + is_compute_bound = true; +} + +void ProgramManager::BindComputeAssemblyProgram(GLuint program) { + if (current_assembly_compute_program != program) { + current_assembly_compute_program = program; + glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program); + } + UnbindPipeline(); +} + +void ProgramManager::BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) { + static constexpr std::array<GLenum, 5> stage_enums{ + GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT, + GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT, + }; + for (size_t stage = 0; stage < NUM_STAGES; ++stage) { + if (current_programs[stage] != programs[stage].handle) { + current_programs[stage] = programs[stage].handle; + glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle); + } + } + BindPipeline(); +} + +void ProgramManager::BindPresentPrograms(GLuint vertex, GLuint fragment) { + if (current_programs[0] != vertex) { + current_programs[0] = vertex; + glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex); + } + if (current_programs[4] != fragment) { + current_programs[4] = fragment; + glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment); + } + glUseProgramStages( + pipeline.handle, + GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0); + current_programs[1] = 0; + current_programs[2] = 0; + current_programs[3] = 0; + + if (current_stage_mask != 0) { + current_stage_mask = 0; + for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) { + glDisable(program_type); + } + } + BindPipeline(); +} + +void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, + u32 stage_mask) { + const u32 changed_mask = current_stage_mask ^ stage_mask; + current_stage_mask = stage_mask; + + if (changed_mask != 0) { + for (size_t stage = 0; stage < NUM_STAGES; ++stage) { + if (((changed_mask >> stage) & 1) != 0) { + if (((stage_mask >> stage) & 1) != 0) { + glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]); + } else { + glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]); + } + } + } + } + for (size_t stage = 0; stage < NUM_STAGES; ++stage) { + if (current_programs[stage] != programs[stage].handle) { + current_programs[stage] = programs[stage].handle; + glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle); + } + } + UnbindPipeline(); +} + +void ProgramManager::RestoreGuestCompute() {} + +void ProgramManager::BindPipeline() { + if (!is_pipeline_bound) { + is_pipeline_bound = true; + glBindProgramPipeline(pipeline.handle); + } + UnbindCompute(); +} + +void ProgramManager::UnbindPipeline() { + if (is_pipeline_bound) { + is_pipeline_bound = false; + glBindProgramPipeline(0); + } + UnbindCompute(); +} + +void ProgramManager::UnbindCompute() { + if (is_compute_bound) { + is_compute_bound = false; + glUseProgram(0); + } +} +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index a84f5aeb3..07ffab77f 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -6,8 +6,6 @@ #include <array> #include <span> -#include <glad/glad.h> - #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_resource_manager.h" @@ -16,121 +14,28 @@ namespace OpenGL { class ProgramManager { static constexpr size_t NUM_STAGES = 5; - static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{ - GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV, - GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, - }; - public: - explicit ProgramManager(const Device& device) { - glCreateProgramPipelines(1, &pipeline.handle); - if (device.UseAssemblyShaders()) { - glEnable(GL_COMPUTE_PROGRAM_NV); - } - } - - void BindComputeProgram(GLuint program) { - glUseProgram(program); - is_compute_bound = true; - } - - void BindComputeAssemblyProgram(GLuint program) { - if (current_assembly_compute_program != program) { - current_assembly_compute_program = program; - glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program); - } - UnbindPipeline(); - } - - void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) { - static constexpr std::array<GLenum, 5> stage_enums{ - GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT, - GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT, - }; - for (size_t stage = 0; stage < NUM_STAGES; ++stage) { - if (current_programs[stage] != programs[stage].handle) { - current_programs[stage] = programs[stage].handle; - glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle); - } - } - BindPipeline(); - } - - void BindPresentPrograms(GLuint vertex, GLuint fragment) { - if (current_programs[0] != vertex) { - current_programs[0] = vertex; - glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex); - } - if (current_programs[4] != fragment) { - current_programs[4] = fragment; - glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment); - } - glUseProgramStages( - pipeline.handle, - GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0); - current_programs[1] = 0; - current_programs[2] = 0; - current_programs[3] = 0; - - if (current_stage_mask != 0) { - current_stage_mask = 0; - for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) { - glDisable(program_type); - } - } - BindPipeline(); - } + explicit ProgramManager(const Device& device); + + void BindComputeProgram(GLuint program); + + void BindComputeAssemblyProgram(GLuint program); + + void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs); + + void BindPresentPrograms(GLuint vertex, GLuint fragment); void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, - u32 stage_mask) { - const u32 changed_mask = current_stage_mask ^ stage_mask; - current_stage_mask = stage_mask; - - if (changed_mask != 0) { - for (size_t stage = 0; stage < NUM_STAGES; ++stage) { - if (((changed_mask >> stage) & 1) != 0) { - if (((stage_mask >> stage) & 1) != 0) { - glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]); - } else { - glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]); - } - } - } - } - for (size_t stage = 0; stage < NUM_STAGES; ++stage) { - if (current_programs[stage] != programs[stage].handle) { - current_programs[stage] = programs[stage].handle; - glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle); - } - } - UnbindPipeline(); - } - - void RestoreGuestCompute() {} + u32 stage_mask); + + void RestoreGuestCompute(); private: - void BindPipeline() { - if (!is_pipeline_bound) { - is_pipeline_bound = true; - glBindProgramPipeline(pipeline.handle); - } - UnbindCompute(); - } - - void UnbindPipeline() { - if (is_pipeline_bound) { - is_pipeline_bound = false; - glBindProgramPipeline(0); - } - UnbindCompute(); - } - - void UnbindCompute() { - if (is_compute_bound) { - is_compute_bound = false; - glUseProgram(0); - } - } + void BindPipeline(); + + void UnbindPipeline(); + + void UnbindCompute(); OGLPipeline pipeline; bool is_pipeline_bound{}; diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 113528e9b..5d9d370f2 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -354,6 +354,7 @@ struct TextureCacheParams { static constexpr bool FRAMEBUFFER_BLITS = true; static constexpr bool HAS_EMULATED_COPIES = true; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; + static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false; using Runtime = OpenGL::TextureCacheRuntime; using Image = OpenGL::Image; @@ -361,6 +362,7 @@ struct TextureCacheParams { using ImageView = OpenGL::ImageView; using Sampler = OpenGL::Sampler; using Framebuffer = OpenGL::Framebuffer; + using AsyncBuffer = u32; }; using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index bc75680f0..2a74c1d05 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -17,8 +17,14 @@ #include "core/frontend/emu_window.h" #include "core/memory.h" #include "core/telemetry_session.h" +#include "video_core/host_shaders/ffx_a_h.h" +#include "video_core/host_shaders/ffx_fsr1_h.h" +#include "video_core/host_shaders/full_screen_triangle_vert.h" #include "video_core/host_shaders/fxaa_frag.h" #include "video_core/host_shaders/fxaa_vert.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" #include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" @@ -31,6 +37,7 @@ #include "video_core/host_shaders/smaa_edge_detection_vert.h" #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" +#include "video_core/renderer_opengl/gl_fsr.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" @@ -268,12 +275,17 @@ void RendererOpenGL::InitOpenGLObjects() { fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); - const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) { - std::string shader_source{specialized_source}; - constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\""; + const auto replace_include = [](std::string& shader_source, std::string_view include_name, + std::string_view include_content) { + const std::string include_string = fmt::format("#include \"{}\"", include_name); const std::size_t pos = shader_source.find(include_string); ASSERT(pos != std::string::npos); - shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL); + shader_source.replace(pos, include_string.size(), include_content); + }; + + const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { + std::string shader_source{specialized_source}; + replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); return CreateProgram(shader_source, stage); }; @@ -298,14 +310,32 @@ void RendererOpenGL::InitOpenGLObjects() { CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), GL_FRAGMENT_SHADER); + std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; + replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); + replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); + + std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; + std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; + replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); + replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); + + fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, + fsr_rcas_frag_source); + // Generate presentation sampler present_sampler.Create(); glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); present_sampler_nn.Create(); glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Generate VBO handle for drawing vertex_buffer.Create(); @@ -442,7 +472,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glBindTextureUnit(0, screen_info.display_texture); - const auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + if (anti_aliasing > Settings::AntiAliasing::LastAA) { + LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); + anti_aliasing = Settings::AntiAliasing::None; + Settings::values.anti_aliasing.SetValue(anti_aliasing); + } + if (anti_aliasing != Settings::AntiAliasing::None) { glEnablei(GL_SCISSOR_TEST, 0); auto viewport_width = screen_info.texture.width; @@ -519,6 +555,31 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glBindTextureUnit(0, aa_texture.handle); } + glDisablei(GL_SCISSOR_TEST, 0); + + if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + if (!fsr->AreBuffersInitialized()) { + fsr->InitBuffers(); + } + + auto crop_rect = framebuffer_crop_rect; + if (crop_rect.GetWidth() == 0) { + crop_rect.right = framebuffer_width; + } + if (crop_rect.GetHeight() == 0) { + crop_rect.bottom = framebuffer_height; + } + crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); + const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width); + const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height); + glBindSampler(0, present_sampler.handle); + fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect); + } else { + if (fsr->AreBuffersInitialized()) { + fsr->ReleaseBuffers(); + } + } + const std::array ortho_matrix = MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); @@ -534,10 +595,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { case Settings::ScalingFilter::ScaleForce: return present_scaleforce_fragment.handle; case Settings::ScalingFilter::Fsr: - LOG_WARNING( - Render_OpenGL, - "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce"); - return present_scaleforce_fragment.handle; + return fsr->GetPresentFragmentProgram().handle; default: return present_bilinear_fragment.handle; } @@ -572,15 +630,18 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); f32 scale_v = static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); - // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering - // (e.g. handheld mode) on a 1920x1080 framebuffer. - if (framebuffer_crop_rect.GetWidth() > 0) { - scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / - static_cast<f32>(screen_info.texture.width); - } - if (framebuffer_crop_rect.GetHeight() > 0) { - scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / - static_cast<f32>(screen_info.texture.height); + + if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) { + // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering + // (e.g. handheld mode) on a 1920x1080 framebuffer. + if (framebuffer_crop_rect.GetWidth() > 0) { + scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / + static_cast<f32>(screen_info.texture.width); + } + if (framebuffer_crop_rect.GetHeight() > 0) { + scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / + static_cast<f32>(screen_info.texture.height); + } } if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && !screen_info.was_accelerated) { @@ -606,7 +667,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { } else { glDisable(GL_FRAMEBUFFER_SRGB); } - glDisablei(GL_SCISSOR_TEST, 0); glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), static_cast<GLfloat>(layout.height)); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index cc97d7b26..f1d5fd954 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -10,6 +10,7 @@ #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_device.h" +#include "video_core/renderer_opengl/gl_fsr.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -141,6 +142,8 @@ private: OGLTexture smaa_edges_tex; OGLTexture smaa_blend_tex; + std::unique_ptr<FSR> fsr; + /// OpenGL framebuffer data std::vector<u8> gl_framebuffer_data; diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 3f2b139e0..cf2964a3f 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -4,14 +4,16 @@ #include <algorithm> #include "common/settings.h" +#include "video_core/host_shaders/blit_color_float_frag_spv.h" #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" -#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" +#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h" +#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h" #include "video_core/renderer_vulkan/blit_image.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -69,10 +71,11 @@ constexpr VkDescriptorSetLayoutCreateInfo TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CRE .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()), .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(), }; -constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{ - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, +template <VkShaderStageFlags stageFlags, size_t size> +inline constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{ + .stageFlags = stageFlags, .offset = 0, - .size = sizeof(PushConstants), + .size = static_cast<u32>(size), }; constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, @@ -125,10 +128,8 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE .alphaToCoverageEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE, }; -constexpr std::array DYNAMIC_STATES{ - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, -}; +constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_BLEND_CONSTANTS}; constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, @@ -205,15 +206,15 @@ inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{ }; constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo( - const VkDescriptorSetLayout* set_layout) { + const VkDescriptorSetLayout* set_layout, vk::Span<VkPushConstantRange> push_constants) { return VkPipelineLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, - .setLayoutCount = 1, + .setLayoutCount = (set_layout != nullptr ? 1u : 0u), .pSetLayouts = set_layout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &PUSH_CONSTANT_RANGE, + .pushConstantRangeCount = push_constants.size(), + .pPushConstantRanges = push_constants.data(), }; } @@ -302,8 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr); } -void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, - const Region2D& src_region) { +void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) { const VkOffset2D offset{ .x = std::min(dst_region.start.x, dst_region.end.x), .y = std::min(dst_region.start.y, dst_region.end.y), @@ -325,15 +325,23 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi .offset = offset, .extent = extent, }; - const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x); - const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y); + cmdbuf.SetViewport(0, viewport); + cmdbuf.SetScissor(0, scissor); +} + +void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, + const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) { + BindBlitState(cmdbuf, dst_region); + const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) / + static_cast<float>(src_size.width); + const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) / + static_cast<float>(src_size.height); const PushConstants push_constants{ .tex_scale = {scale_x, scale_y}, - .tex_offset = {static_cast<float>(src_region.start.x), - static_cast<float>(src_region.start.y)}, + .tex_offset = {static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width), + static_cast<float>(src_region.start.y) / + static_cast<float>(src_size.height)}, }; - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, scissor); cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); } @@ -347,6 +355,51 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) { .height = is_rescaled ? resolution.ScaleUp(height) : height, }; } + +void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, + VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { + constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = flags, + .dstAccessMask = flags, + .oldLayout = source_layout, + .newLayout = target_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, barrier); +} + +void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) { + const VkRenderPass render_pass = framebuffer->RenderPass(); + const VkFramebuffer framebuffer_handle = framebuffer->Handle(); + const VkExtent2D render_area = framebuffer->RenderArea(); + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = render_pass, + .framebuffer = framebuffer_handle, + .renderArea{ + .offset{}, + .extent = render_area, + }, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); +} } // Anonymous namespace BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, @@ -360,13 +413,20 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, two_textures_descriptor_allocator{ descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, - one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout( - PipelineLayoutCreateInfo(one_texture_set_layout.address()))), - two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( - PipelineLayoutCreateInfo(two_textures_set_layout.address()))), + one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( + one_texture_set_layout.address(), + PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))), + two_textures_pipeline_layout( + device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( + two_textures_set_layout.address(), + PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))), + clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( + nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))), full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), - blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), + blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)), blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), + clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)), + clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)), convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), @@ -404,6 +464,32 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView scheduler.InvalidateState(); } +void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, + VkImage src_image, VkSampler src_sampler, + const Region2D& dst_region, const Region2D& src_region, + const Extent3D& src_size) { + const BlitImagePipelineKey key{ + .renderpass = dst_framebuffer->RenderPass(), + .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy, + }; + const VkPipelineLayout layout = *one_texture_pipeline_layout; + const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, + src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL); + BeginRenderPass(cmdbuf, dst_framebuffer); + const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); + UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, + nullptr); + BindBlitState(cmdbuf, layout, dst_region, src_region, src_size); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + }); +} + void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view, VkImageView src_stencil_view, const Region2D& dst_region, const Region2D& src_region, @@ -479,6 +565,30 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); } +void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask, + const std::array<f32, 4>& clear_color, + const Region2D& dst_region) { + const BlitImagePipelineKey key{ + .renderpass = dst_framebuffer->RenderPass(), + .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult, + }; + const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key); + const VkPipelineLayout layout = *clear_color_pipeline_layout; + scheduler.RequestRenderpass(dst_framebuffer); + scheduler.Record( + [pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) { + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + const std::array blend_color = { + (color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f, + (color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f}; + cmdbuf.SetBlendConstants(blend_color.data()); + BindBlitState(cmdbuf, dst_region); + cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color); + cmdbuf.Draw(3, 1, 0, 0); + }); + scheduler.InvalidateState(); +} + void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { const VkPipelineLayout layout = *one_texture_pipeline_layout; @@ -654,6 +764,58 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip return *blit_depth_stencil_pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) { + const auto it = std::ranges::find(clear_color_keys, key); + if (it != clear_color_keys.end()) { + return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)]; + } + clear_color_keys.push_back(key); + const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag); + const VkPipelineColorBlendAttachmentState color_blend_attachment_state{ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_CLEAR, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment_state, + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; + clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast<u32>(stages.size()), + .pStages = stages.data(), + .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .pTessellationState = nullptr, + .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .pColorBlendState = &color_blend_state_generic_create_info, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = *clear_color_pipeline_layout, + .renderPass = key.renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + })); + return *clear_color_pipelines.back(); +} + void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth) { if (pipeline) { diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 5df679fb4..2976a7d91 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -10,6 +10,8 @@ namespace Vulkan { +using VideoCommon::Extent3D; +using VideoCommon::Offset2D; using VideoCommon::Region2D; class Device; @@ -36,6 +38,10 @@ public: Tegra::Engines::Fermi2D::Filter filter, Tegra::Engines::Fermi2D::Operation operation); + void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, + 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, @@ -55,6 +61,9 @@ public: void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); + void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask, + const std::array<f32, 4>& clear_color, const Region2D& dst_region); + private: void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view); @@ -66,6 +75,8 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key); + void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); @@ -91,9 +102,12 @@ private: DescriptorAllocator two_textures_descriptor_allocator; vk::PipelineLayout one_texture_pipeline_layout; vk::PipelineLayout two_textures_pipeline_layout; + vk::PipelineLayout clear_color_pipeline_layout; vk::ShaderModule full_screen_vert; vk::ShaderModule blit_color_to_color_frag; vk::ShaderModule blit_depth_stencil_frag; + vk::ShaderModule clear_color_vert; + vk::ShaderModule clear_color_frag; vk::ShaderModule convert_depth_to_float_frag; vk::ShaderModule convert_float_to_depth_frag; vk::ShaderModule convert_abgr8_to_d24s8_frag; @@ -106,6 +120,8 @@ private: std::vector<vk::Pipeline> blit_color_pipelines; std::vector<BlitImagePipelineKey> blit_depth_stencil_keys; std::vector<vk::Pipeline> blit_depth_stencil_pipelines; + std::vector<BlitImagePipelineKey> clear_color_keys; + std::vector<vk::Pipeline> clear_color_pipelines; vk::Pipeline convert_d32_to_r32_pipeline; vk::Pipeline convert_r32_to_d32_pipeline; vk::Pipeline convert_d16_to_r16_pipeline; diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index e62b36822..f8398b511 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -48,43 +48,30 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& } } // Anonymous namespace -void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, - bool has_extended_dynamic_state, bool has_dynamic_vertex_input) { +void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) { const Maxwell& regs = maxwell3d.regs; const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; - const std::array enabled_lut{ - regs.polygon_offset_point_enable, - regs.polygon_offset_line_enable, - regs.polygon_offset_fill_enable, - }; - const u32 topology_index = static_cast<u32>(topology_); raw1 = 0; - extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); - dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); + extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0); + extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0); + extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0); + extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0); + extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0); + dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0); - primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); - depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumZ); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); - patch_control_points_minus_one.Assign(regs.patch_vertices - 1); tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == Maxwell::Tessellation::OutputPrimitives::Triangles_CW); - logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - logic_op.Assign(PackLogicOp(regs.logic_op.op)); + patch_control_points_minus_one.Assign(regs.patch_vertices - 1); topology.Assign(topology_); msaa_mode.Assign(regs.anti_alias_samples_mode); raw2 = 0; - rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); + const auto test_func = regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); @@ -97,6 +84,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + app_stage.Assign(maxwell3d.engine_state); for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast<u8>(regs.rt[i].format); @@ -105,7 +93,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, point_size = Common::BitCast<u32>(regs.point_size); if (maxwell3d.dirty.flags[Dirty::VertexInput]) { - if (has_dynamic_vertex_input) { + if (features.has_dynamic_vertex_input) { // Dirty flag will be reset by the command buffer update static constexpr std::array LUT{ 0u, // Invalid @@ -144,12 +132,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, } } } - if (maxwell3d.dirty.flags[Dirty::Blending]) { - maxwell3d.dirty.flags[Dirty::Blending] = false; - for (size_t index = 0; index < attachments.size(); ++index) { - attachments[index].Refresh(regs, index); - } - } if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) { maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false; const auto& transform = regs.viewport_transform; @@ -157,8 +139,27 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, return static_cast<u16>(viewport.swizzle.raw); }); } + dynamic_state.raw1 = 0; + dynamic_state.raw2 = 0; if (!extended_dynamic_state) { dynamic_state.Refresh(regs); + std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { + return static_cast<u16>(array.stride.Value()); + }); + } + if (!extended_dynamic_state_2_extra) { + dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); + } + if (!extended_dynamic_state_3_blend) { + if (maxwell3d.dirty.flags[Dirty::Blending]) { + maxwell3d.dirty.flags[Dirty::Blending] = false; + for (size_t index = 0; index < attachments.size(); ++index) { + attachments[index].Refresh(regs, index); + } + } + } + if (!extended_dynamic_state_3_enables) { + dynamic_state.Refresh3(regs); } if (xfb_enabled) { RefreshXfbState(xfb_state, regs); @@ -175,12 +176,11 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t mask_a.Assign(mask.A); // TODO: C++20 Use templated lambda to deduplicate code + if (!regs.blend.enable[index]) { + return; + } - if (!regs.blend_per_target_enabled) { - if (!regs.blend.enable[index]) { - return; - } - const auto& src = regs.blend; + const auto setup_blend = [&]<typename T>(const T& src) { equation_rgb.Assign(PackBlendEquation(src.color_op)); equation_a.Assign(PackBlendEquation(src.alpha_op)); factor_source_rgb.Assign(PackBlendFactor(src.color_source)); @@ -188,20 +188,13 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t factor_source_a.Assign(PackBlendFactor(src.alpha_source)); factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); enable.Assign(1); - return; - } + }; - if (!regs.blend.enable[index]) { + if (!regs.blend_per_target_enabled) { + setup_blend(regs.blend); return; } - const auto& src = regs.blend_per_target[index]; - equation_rgb.Assign(PackBlendEquation(src.color_op)); - equation_a.Assign(PackBlendEquation(src.alpha_op)); - factor_source_rgb.Assign(PackBlendFactor(src.color_source)); - factor_dest_rgb.Assign(PackBlendFactor(src.color_dest)); - factor_source_a.Assign(PackBlendFactor(src.alpha_source)); - factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); - enable.Assign(1); + setup_blend(regs.blend_per_target[index]); } void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { @@ -211,8 +204,6 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { packed_front_face = 1 - packed_front_face; } - raw1 = 0; - raw2 = 0; front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); @@ -236,9 +227,37 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); cull_face.Assign(PackCullFace(regs.gl_cull_face)); cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); - std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { - return static_cast<u16>(array.stride.Value()); - }); +} + +void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, + Maxwell::PrimitiveTopology topology_, + bool base_feautures_supported) { + logic_op.Assign(PackLogicOp(regs.logic_op.op)); + + if (base_feautures_supported) { + return; + } + + const std::array enabled_lut{ + regs.polygon_offset_point_enable, + regs.polygon_offset_line_enable, + regs.polygon_offset_fill_enable, + }; + const u32 topology_index = static_cast<u32>(topology_); + + rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); + primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); + depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); +} + +void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { + logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); } size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index ab79fb8f3..98ea20b42 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -17,6 +17,15 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; +struct DynamicFeatures { + bool has_extended_dynamic_state; + bool has_extended_dynamic_state_2; + bool has_extended_dynamic_state_2_extra; + bool has_extended_dynamic_state_3_blend; + bool has_extended_dynamic_state_3_enables; + bool has_dynamic_vertex_input; +}; + struct FixedPipelineState { static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; @@ -133,6 +142,17 @@ struct FixedPipelineState { struct DynamicState { union { u32 raw1; + BitField<0, 2, u32> cull_face; + BitField<2, 1, u32> cull_enable; + BitField<3, 1, u32> primitive_restart_enable; + BitField<4, 1, u32> depth_bias_enable; + BitField<5, 1, u32> rasterize_enable; + BitField<6, 4, u32> logic_op; + BitField<10, 1, u32> logic_op_enable; + BitField<11, 1, u32> depth_clamp_disabled; + }; + union { + u32 raw2; StencilFace<0> front; StencilFace<12> back; BitField<24, 1, u32> stencil_enable; @@ -142,15 +162,11 @@ struct FixedPipelineState { BitField<28, 1, u32> front_face; BitField<29, 3, u32> depth_test_func; }; - union { - u32 raw2; - BitField<0, 2, u32> cull_face; - BitField<2, 1, u32> cull_enable; - }; - // Vertex stride is a 12 bits value, we have 4 bits to spare per element - std::array<u16, Maxwell::NumVertexArrays> vertex_strides; void Refresh(const Maxwell& regs); + void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, + bool base_feautures_supported); + void Refresh3(const Maxwell& regs); Maxwell::ComparisonOp DepthTestFunc() const noexcept { return UnpackComparisonOp(depth_test_func); @@ -168,25 +184,24 @@ struct FixedPipelineState { union { u32 raw1; BitField<0, 1, u32> extended_dynamic_state; - BitField<1, 1, u32> dynamic_vertex_input; - BitField<2, 1, u32> xfb_enabled; - BitField<3, 1, u32> primitive_restart_enable; - BitField<4, 1, u32> depth_bias_enable; - BitField<5, 1, u32> depth_clamp_disabled; - BitField<6, 1, u32> ndc_minus_one_to_one; - BitField<7, 2, u32> polygon_mode; - BitField<9, 5, u32> patch_control_points_minus_one; - BitField<14, 2, u32> tessellation_primitive; - BitField<16, 2, u32> tessellation_spacing; - BitField<18, 1, u32> tessellation_clockwise; - BitField<19, 1, u32> logic_op_enable; - BitField<20, 4, u32> logic_op; + BitField<1, 1, u32> extended_dynamic_state_2; + BitField<2, 1, u32> extended_dynamic_state_2_extra; + BitField<3, 1, u32> extended_dynamic_state_3_blend; + BitField<4, 1, u32> extended_dynamic_state_3_enables; + BitField<5, 1, u32> dynamic_vertex_input; + BitField<6, 1, u32> xfb_enabled; + BitField<7, 1, u32> ndc_minus_one_to_one; + BitField<8, 2, u32> polygon_mode; + BitField<10, 2, u32> tessellation_primitive; + BitField<12, 2, u32> tessellation_spacing; + BitField<14, 1, u32> tessellation_clockwise; + BitField<15, 5, u32> patch_control_points_minus_one; + BitField<24, 4, Maxwell::PrimitiveTopology> topology; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; - BitField<0, 1, u32> rasterize_enable; BitField<1, 3, u32> alpha_test_func; BitField<4, 1, u32> early_z; BitField<5, 1, u32> depth_enabled; @@ -197,25 +212,28 @@ struct FixedPipelineState { BitField<14, 1, u32> smooth_lines; BitField<15, 1, u32> alpha_to_coverage_enabled; BitField<16, 1, u32> alpha_to_one_enabled; + BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; }; std::array<u8, Maxwell::NumRenderTargets> color_formats; u32 alpha_test_ref; u32 point_size; - std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; std::array<u16, Maxwell::NumViewports> viewport_swizzles; union { u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state u64 enabled_divisors; }; + + DynamicState dynamic_state; + std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; std::array<u32, Maxwell::NumVertexArrays> binding_divisors; + // Vertex stride is a 12 bits value, we have 4 bits to spare per element + std::array<u16, Maxwell::NumVertexArrays> vertex_strides; - DynamicState dynamic_state; VideoCommon::TransformFeedbackState xfb_state; - void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state, - bool has_dynamic_vertex_input); + void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features); size_t Hash() const noexcept; @@ -230,13 +248,17 @@ struct FixedPipelineState { // When transform feedback is enabled, use the whole struct return sizeof(*this); } - if (dynamic_vertex_input) { + if (dynamic_vertex_input && extended_dynamic_state_3_blend) { // Exclude dynamic state and attributes + return offsetof(FixedPipelineState, dynamic_state); + } + if (dynamic_vertex_input) { + // Exclude dynamic state return offsetof(FixedPipelineState, attributes); } if (extended_dynamic_state) { // Exclude dynamic state - return offsetof(FixedPipelineState, dynamic_state); + return offsetof(FixedPipelineState, vertex_strides); } // Default return offsetof(FixedPipelineState, xfb_state); diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 3e03c5cd6..ca52e2389 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -301,6 +301,8 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; case Maxwell::PrimitiveTopology::Lines: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case Maxwell::PrimitiveTopology::LineLoop: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; case Maxwell::PrimitiveTopology::LineStrip: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; case Maxwell::PrimitiveTopology::Triangles: @@ -309,15 +311,28 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; case Maxwell::PrimitiveTopology::TriangleFan: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case Maxwell::PrimitiveTopology::LinesAdjacency: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY; case Maxwell::PrimitiveTopology::Quads: - // TODO(Rodrigo): Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT whenever it releases + case Maxwell::PrimitiveTopology::QuadStrip: + // TODO: Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT/VK_PRIMITIVE_TOPOLOGY_QUAD_STRIP_EXT + // whenever it releases return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; case Maxwell::PrimitiveTopology::Patches: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; - default: - UNIMPLEMENTED_MSG("Unimplemented topology={}", topology); - return {}; + case Maxwell::PrimitiveTopology::Polygon: + LOG_WARNING(Render_Vulkan, "Draw mode is Polygon with a polygon mode of lines should be a " + "single body and not a bunch of triangles."); + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; } + UNIMPLEMENTED_MSG("Unimplemented topology={}", topology); + return {}; } VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index f502a7d09..2a8d9e377 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -60,24 +60,13 @@ std::string GetDriverVersion(const Device& device) { return GetReadableVersion(version); } -std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) { - std::sort(std::begin(available_extensions), std::end(available_extensions)); - - static constexpr std::size_t AverageExtensionSize = 64; - std::string separated_extensions; - separated_extensions.reserve(available_extensions.size() * AverageExtensionSize); - - const auto end = std::end(available_extensions); - for (auto extension = std::begin(available_extensions); extension != end; ++extension) { - if (const bool is_last = extension + 1 == end; is_last) { - separated_extensions += *extension; - } else { - separated_extensions += fmt::format("{},", *extension); - } - } - return separated_extensions; +std::string BuildCommaSeparatedExtensions( + const std::set<std::string, std::less<>>& available_extensions) { + return fmt::format("{}", fmt::join(available_extensions, ",")); } +} // Anonymous namespace + Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, VkSurfaceKHR surface) { const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices(); @@ -89,7 +78,6 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl const vk::PhysicalDevice physical_device(devices[device_index], dld); return Device(*instance, physical_device, surface, dld); } -} // Anonymous namespace RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, Core::Frontend::EmuWindow& emu_window, @@ -98,7 +86,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, - true, Settings::values.renderer_debug.GetValue())), + Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), @@ -109,6 +97,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, screen_info), rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, state_tracker, scheduler) { + if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { + turbo_mode.emplace(instance, dld); + scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); + } Report(); } catch (const vk::Exception& exception) { LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); @@ -116,6 +108,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, } RendererVulkan::~RendererVulkan() { + scheduler.RegisterOnSubmit([] {}); void(device.GetLogical().WaitIdle()); } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index e7bfecb20..009e75e0d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -13,6 +13,7 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" #include "video_core/renderer_vulkan/vk_swapchain.h" +#include "video_core/renderer_vulkan/vk_turbo_mode.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" @@ -31,6 +32,9 @@ class GPU; namespace Vulkan { +Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, + VkSurfaceKHR surface); + class RendererVulkan final : public VideoCore::RendererBase { public: explicit RendererVulkan(Core::TelemetrySession& telemtry_session, @@ -74,6 +78,7 @@ private: Swapchain swapchain; BlitScreen blit_screen; RasterizerVulkan rasterizer; + std::optional<TurboMode> turbo_mode; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 84d36fea6..1cfb4c2ff 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -51,21 +51,13 @@ size_t BytesPerIndex(VkIndexType index_type) { } } -template <typename T> -std::array<T, 6> MakeQuadIndices(u32 quad, u32 first) { - std::array<T, 6> indices{0, 1, 2, 0, 2, 3}; - for (T& index : indices) { - index = static_cast<T>(first + index + quad * 4); - } - return indices; -} - vk::Buffer CreateBuffer(const Device& device, u64 size) { VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; if (device.IsExtTransformFeedbackSupported()) { flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; } @@ -123,6 +115,187 @@ VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat return *views.back().handle; } +class QuadIndexBuffer { +public: + QuadIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_, + Scheduler& scheduler_, StagingBufferPool& staging_pool_) + : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, + staging_pool{staging_pool_} {} + + virtual ~QuadIndexBuffer() = default; + + void UpdateBuffer(u32 num_indices_) { + if (num_indices_ <= num_indices) { + return; + } + + scheduler.Finish(); + + num_indices = num_indices_; + index_type = IndexTypeFromNumElements(device, num_indices); + + const u32 num_quads = GetQuadsNum(num_indices); + const u32 num_triangle_indices = num_quads * 6; + const u32 num_first_offset_copies = 4; + const size_t bytes_per_index = BytesPerIndex(index_type); + const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies; + buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size_bytes, + .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }); + if (device.HasDebuggingToolAttached()) { + buffer.SetObjectNameEXT("Quad LUT"); + } + memory_commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); + + const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload); + u8* staging_data = staging.mapped_span.data(); + const size_t quad_size = bytes_per_index * 6; + + for (u32 first = 0; first < num_first_offset_copies; ++first) { + for (u32 quad = 0; quad < num_quads; ++quad) { + MakeAndUpdateIndices(staging_data, quad_size, quad, first); + staging_data += quad_size; + } + } + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, + dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) { + const VkBufferCopy copy{ + .srcOffset = src_offset, + .dstOffset = 0, + .size = size_bytes, + }; + const VkBufferMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = dst_buffer, + .offset = 0, + .size = size_bytes, + }; + cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier); + }); + } + + void BindBuffer(u32 first) { + const VkIndexType index_type_ = index_type; + const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices); + const size_t offset = + (sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type); + scheduler.Record([buffer = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) { + cmdbuf.BindIndexBuffer(buffer, offset, index_type_); + }); + } + +protected: + virtual u32 GetQuadsNum(u32 num_indices) const = 0; + + virtual void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) = 0; + + const Device& device; + MemoryAllocator& memory_allocator; + Scheduler& scheduler; + StagingBufferPool& staging_pool; + + vk::Buffer buffer{}; + MemoryCommit memory_commit{}; + VkIndexType index_type{}; + u32 num_indices = 0; +}; + +class QuadArrayIndexBuffer : public QuadIndexBuffer { +public: + QuadArrayIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_, + Scheduler& scheduler_, StagingBufferPool& staging_pool_) + : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {} + + ~QuadArrayIndexBuffer() = default; + +private: + u32 GetQuadsNum(u32 num_indices_) const override { + return num_indices_ / 4; + } + + template <typename T> + static std::array<T, 6> MakeIndices(u32 quad, u32 first) { + std::array<T, 6> indices{0, 1, 2, 0, 2, 3}; + for (T& index : indices) { + index = static_cast<T>(first + index + quad * 4); + } + return indices; + } + + void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) { + switch (index_type) { + case VK_INDEX_TYPE_UINT8_EXT: + std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), quad_size); + break; + case VK_INDEX_TYPE_UINT16: + std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), quad_size); + break; + case VK_INDEX_TYPE_UINT32: + std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), quad_size); + break; + default: + ASSERT(false); + break; + } + } +}; + +class QuadStripIndexBuffer : public QuadIndexBuffer { +public: + QuadStripIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_, + Scheduler& scheduler_, StagingBufferPool& staging_pool_) + : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {} + + ~QuadStripIndexBuffer() = default; + +private: + u32 GetQuadsNum(u32 num_indices_) const override { + return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0; + } + + template <typename T> + static std::array<T, 6> MakeIndices(u32 quad, u32 first) { + std::array<T, 6> indices{0, 3, 1, 0, 2, 3}; + for (T& index : indices) { + index = static_cast<T>(first + index + quad * 2); + } + return indices; + } + + void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) { + switch (index_type) { + case VK_INDEX_TYPE_UINT8_EXT: + std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), quad_size); + break; + case VK_INDEX_TYPE_UINT16: + std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), quad_size); + break; + case VK_INDEX_TYPE_UINT32: + std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), quad_size); + break; + default: + ASSERT(false); + break; + } + } +}; + BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, UpdateDescriptorQueue& update_descriptor_queue_, @@ -130,7 +303,12 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, staging_pool{staging_pool_}, update_descriptor_queue{update_descriptor_queue_}, uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), - quad_index_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue) {} + quad_index_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue) { + quad_array_index_buffer = std::make_shared<QuadArrayIndexBuffer>(device_, memory_allocator_, + scheduler_, staging_pool_); + quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_, + scheduler_, staging_pool_); +} StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) { return staging_pool.Request(size, MemoryUsage::Upload); @@ -152,12 +330,19 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +u32 BufferCacheRuntime::GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetStorageBufferAlignment()); +} + void BufferCacheRuntime::Finish() { scheduler.Finish(); } void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, std::span<const VideoCommon::BufferCopy> copies, bool barrier) { + if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { + return; + } static constexpr VkMemoryBarrier READ_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, @@ -216,6 +401,9 @@ void BufferCacheRuntime::PostCopyBarrier() { } void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) { + if (dest_buffer == VK_NULL_HANDLE) { + return; + } static constexpr VkMemoryBarrier READ_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, @@ -245,10 +433,11 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format); VkDeviceSize vk_offset = offset; VkBuffer vk_buffer = buffer; - if (topology == PrimitiveTopology::Quads) { + if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) { vk_index_type = VK_INDEX_TYPE_UINT32; std::tie(vk_buffer, vk_offset) = - quad_index_pass.Assemble(index_format, num_indices, base_vertex, buffer, offset); + quad_index_pass.Assemble(index_format, num_indices, base_vertex, buffer, offset, + topology == PrimitiveTopology::QuadStrip); } else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) { vk_index_type = VK_INDEX_TYPE_UINT16; std::tie(vk_buffer, vk_offset) = uint8_pass.Assemble(num_indices, buffer, offset); @@ -263,7 +452,7 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat }); } -void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { +void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) { if (count == 0) { ReserveNullBuffer(); scheduler.Record([this](vk::CommandBuffer cmdbuf) { @@ -271,16 +460,14 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { }); return; } - ReserveQuadArrayLUT(first + count, true); - - // The LUT has the indices 0, 1, 2, and 3 copied as an array - // To apply these 'first' offsets we can apply an offset based on the modulus. - const VkIndexType index_type = quad_array_lut_index_type; - const size_t sub_first_offset = static_cast<size_t>(first % 4) * (current_num_indices / 4); - const size_t offset = (sub_first_offset + first / 4) * 6ULL * BytesPerIndex(index_type); - scheduler.Record([buffer = *quad_array_lut, index_type, offset](vk::CommandBuffer cmdbuf) { - cmdbuf.BindIndexBuffer(buffer, offset, index_type); - }); + + if (topology == PrimitiveTopology::Quads) { + quad_array_index_buffer->UpdateBuffer(first + count); + quad_array_index_buffer->BindBuffer(first); + } else if (topology == PrimitiveTopology::QuadStrip) { + quad_strip_index_buffer->UpdateBuffer(first + count); + quad_strip_index_buffer->BindBuffer(first); + } } void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, @@ -296,6 +483,11 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); }); } else { + if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { + ReserveNullBuffer(); + buffer = *null_buffer; + offset = 0; + } scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { cmdbuf.BindVertexBuffer(index, buffer, offset); }); @@ -323,83 +515,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, }); } -void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) { - if (num_indices <= current_num_indices) { - return; - } - if (wait_for_idle) { - scheduler.Finish(); - } - current_num_indices = num_indices; - quad_array_lut_index_type = IndexTypeFromNumElements(device, num_indices); - - const u32 num_quads = num_indices / 4; - const u32 num_triangle_indices = num_quads * 6; - const u32 num_first_offset_copies = 4; - const size_t bytes_per_index = BytesPerIndex(quad_array_lut_index_type); - const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies; - quad_array_lut = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = size_bytes, - .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }); - if (device.HasDebuggingToolAttached()) { - quad_array_lut.SetObjectNameEXT("Quad LUT"); - } - quad_array_lut_commit = memory_allocator.Commit(quad_array_lut, MemoryUsage::DeviceLocal); - - const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload); - u8* staging_data = staging.mapped_span.data(); - const size_t quad_size = bytes_per_index * 6; - for (u32 first = 0; first < num_first_offset_copies; ++first) { - for (u32 quad = 0; quad < num_quads; ++quad) { - switch (quad_array_lut_index_type) { - case VK_INDEX_TYPE_UINT8_EXT: - std::memcpy(staging_data, MakeQuadIndices<u8>(quad, first).data(), quad_size); - break; - case VK_INDEX_TYPE_UINT16: - std::memcpy(staging_data, MakeQuadIndices<u16>(quad, first).data(), quad_size); - break; - case VK_INDEX_TYPE_UINT32: - std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size); - break; - default: - ASSERT(false); - break; - } - staging_data += quad_size; - } - } - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, - dst_buffer = *quad_array_lut, size_bytes](vk::CommandBuffer cmdbuf) { - const VkBufferCopy copy{ - .srcOffset = src_offset, - .dstOffset = 0, - .size = size_bytes, - }; - const VkBufferMemoryBarrier write_barrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = dst_buffer, - .offset = 0, - .size = size_bytes, - }; - cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, - 0, write_barrier); - }); -} - void BufferCacheRuntime::ReserveNullBuffer() { if (null_buffer) { return; @@ -417,6 +532,7 @@ void BufferCacheRuntime::ReserveNullBuffer() { if (device.IsExtTransformFeedbackSupported()) { create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; } + create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; null_buffer = device.GetLogical().CreateBuffer(create_info); if (device.HasDebuggingToolAttached()) { null_buffer.SetObjectNameEXT("Null buffer"); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index a15c8b39b..06539c733 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -50,6 +50,9 @@ private: std::vector<BufferView> views; }; +class QuadArrayIndexBuffer; +class QuadStripIndexBuffer; + class BufferCacheRuntime { friend Buffer; @@ -70,6 +73,8 @@ public: bool CanReportMemoryUsage() const; + u32 GetStorageBufferAlignment() const; + [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); @@ -86,7 +91,7 @@ public: void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices, u32 base_vertex, VkBuffer buffer, u32 offset, u32 size); - void BindQuadArrayIndexBuffer(u32 first, u32 count); + void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); @@ -118,8 +123,6 @@ private: update_descriptor_queue.AddBuffer(buffer, offset, size); } - void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle); - void ReserveNullBuffer(); const Device& device; @@ -128,10 +131,8 @@ private: StagingBufferPool& staging_pool; UpdateDescriptorQueue& update_descriptor_queue; - vk::Buffer quad_array_lut; - MemoryCommit quad_array_lut_commit; - VkIndexType quad_array_lut_index_type{}; - u32 current_num_indices = 0; + std::shared_ptr<QuadArrayIndexBuffer> quad_array_index_buffer; + std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer; vk::Buffer null_buffer; MemoryCommit null_buffer_commit; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 2c00979d7..1a316b6eb 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -245,7 +245,7 @@ QuadIndexedPass::QuadIndexedPass(const Device& device_, Scheduler& scheduler_, UpdateDescriptorQueue& update_descriptor_queue_) : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE<sizeof(u32) * 2>, VULKAN_QUAD_INDEXED_COMP_SPV), + COMPUTE_PUSH_CONSTANT_RANGE<sizeof(u32) * 3>, VULKAN_QUAD_INDEXED_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, update_descriptor_queue{update_descriptor_queue_} {} @@ -253,7 +253,7 @@ QuadIndexedPass::~QuadIndexedPass() = default; std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, u32 base_vertex, - VkBuffer src_buffer, u32 src_offset) { + VkBuffer src_buffer, u32 src_offset, bool is_strip) { const u32 index_shift = [index_format] { switch (index_format) { case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedByte: @@ -267,7 +267,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( return 2; }(); const u32 input_size = num_vertices << index_shift; - const u32 num_tri_vertices = (num_vertices / 4) * 6; + const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6; const std::size_t staging_size = num_tri_vertices * sizeof(u32); const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); @@ -278,8 +278,8 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( const void* const descriptor_data{update_descriptor_queue.UpdateData()}; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, - index_shift](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, index_shift, + is_strip](vk::CommandBuffer cmdbuf) { static constexpr u32 DISPATCH_SIZE = 1024; static constexpr VkMemoryBarrier WRITE_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, @@ -287,7 +287,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, }; - const std::array<u32, 2> push_constants{base_vertex, index_shift}; + const std::array<u32, 3> push_constants{base_vertex, index_shift, is_strip ? 1u : 0u}; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 5d32e3caf..c4c8fa081 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -74,7 +74,7 @@ public: std::pair<VkBuffer, VkDeviceSize> Assemble( Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, - u32 base_vertex, VkBuffer src_buffer, u32 src_offset); + u32 base_vertex, VkBuffer src_buffer, u32 src_offset, bool is_strip); private: Scheduler& scheduler; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 04a3a861e..2a0f0dbf0 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -24,13 +24,15 @@ using Shader::ImageBufferDescriptor; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; -ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, +ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, vk::ShaderModule spv_module_) - : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, + : device{device_}, pipeline_cache(pipeline_cache_), + update_descriptor_queue{update_descriptor_queue_}, info{info_}, spv_module(std::move(spv_module_)) { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -56,23 +58,27 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } - pipeline = device.GetLogical().CreateComputePipeline({ - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = flags, - .stage{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = *spv_module, - .pName = "main", - .pSpecializationInfo = nullptr, + pipeline = device.GetLogical().CreateComputePipeline( + { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = flags, + .stage{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = + device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = *spv_module, + .pName = "main", + .pSpecializationInfo = nullptr, + }, + .layout = *pipeline_layout, + .basePipelineHandle = 0, + .basePipelineIndex = 0, }, - .layout = *pipeline_layout, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }); + *pipeline_cache); + if (pipeline_statistics) { pipeline_statistics->Collect(*pipeline); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index d70837fc5..78d77027f 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -28,7 +28,8 @@ class Scheduler; class ComputePipeline { public: - explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, + explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, @@ -46,6 +47,7 @@ public: private: const Device& device; + vk::PipelineCache& pipeline_cache; UpdateDescriptorQueue& update_descriptor_queue; Shader::Info info; diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp index 33daa8c1c..df972cd54 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <cmath> -#include "common/bit_cast.h" #include "common/common_types.h" #include "common/div_ceil.h" #include "common/settings.h" +#include "video_core/fsr.h" #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" @@ -17,146 +16,7 @@ #include "video_core/vulkan_common/vulkan_device.h" namespace Vulkan { -namespace { -// Reimplementations of the constant generating functions in ffx_fsr1.h -// GCC generated a lot of warnings when using the official header. -u32 AU1_AH1_AF1(f32 f) { - static constexpr u32 base[512]{ - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, - 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000, - 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00, - 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008, - 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400, - 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, - 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, - 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - }; - static constexpr s8 shift[512]{ - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, - 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, - }; - const u32 u = Common::BitCast<u32>(f); - const u32 i = u >> 23; - return base[i] + ((u & 0x7fffff) >> shift[i]); -} - -u32 AU1_AH2_AF2(f32 a[2]) { - return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16); -} - -void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX, - f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, - f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) { - con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX); - con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY); - con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f); - con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f); - con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); - con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY); - con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); - con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY); - con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX); - con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY); - con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX); - con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY); - con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX); - con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY); - con3[2] = con3[3] = 0; -} - -void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], - f32 inputViewportInPixelsX, f32 inputViewportInPixelsY, - f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX, - f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) { - FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, - inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); - con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f + - inputOffsetInPixelsX); - con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f + - inputOffsetInPixelsY); -} - -void FsrRcasCon(u32* con, f32 sharpness) { - sharpness = std::exp2f(-sharpness); - f32 hSharp[2]{sharpness, sharpness}; - con[0] = Common::BitCast<u32>(sharpness); - con[1] = AU1_AH2_AF2(hSharp); - con[2] = 0; - con[3] = 0; -} -} // Anonymous namespace +using namespace FSR; FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, VkExtent2D output_size_) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 515d8d869..f91bb5a1d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -201,6 +201,22 @@ struct SimpleVertexSpec { static constexpr bool has_images = false; }; +struct SimpleStorageSpec { + static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true}; + static constexpr bool has_storage_buffers = true; + static constexpr bool has_texture_buffers = false; + static constexpr bool has_image_buffers = false; + static constexpr bool has_images = false; +}; + +struct SimpleImageSpec { + static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true}; + static constexpr bool has_storage_buffers = false; + static constexpr bool has_texture_buffers = false; + static constexpr bool has_image_buffers = false; + static constexpr bool has_images = true; +}; + struct DefaultSpec { static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true}; static constexpr bool has_storage_buffers = true; @@ -211,19 +227,21 @@ struct DefaultSpec { ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules, const std::array<Shader::Info, NUM_STAGES>& infos) { - return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, DefaultSpec>(modules, infos); + return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, SimpleStorageSpec, SimpleImageSpec, + DefaultSpec>(modules, infos); } } // Anonymous namespace GraphicsPipeline::GraphicsPipeline( Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, - VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, + vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify, + const Device& device_, DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, const std::array<const Shader::Info*, NUM_STAGES>& infos) - : key{key_}, device{device_}, texture_cache{texture_cache_}, - buffer_cache{buffer_cache_}, scheduler{scheduler_}, + : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + pipeline_cache(pipeline_cache_), scheduler{scheduler_}, update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -524,6 +542,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { FixedPipelineState::DynamicState dynamic{}; if (!key.state.extended_dynamic_state) { dynamic = key.state.dynamic_state; + } else { + dynamic.raw1 = key.state.dynamic_state.raw1; } static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; @@ -561,7 +581,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; vertex_bindings.push_back({ .binding = static_cast<u32>(index), - .stride = dynamic.vertex_strides[index], + .stride = key.state.vertex_strides[index], .inputRate = rate, }); if (instanced) { @@ -625,12 +645,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pNext = nullptr, .flags = 0, .topology = input_assembly_topology, - .primitiveRestartEnable = key.state.primitive_restart_enable != 0 && - ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && - device.IsTopologyListPrimitiveRestartSupported()) || - SupportsPrimitiveRestart(input_assembly_topology) || - (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && - device.IsPatchListPrimitiveRestartSupported())), + .primitiveRestartEnable = + dynamic.primitive_restart_enable != 0 && + ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsTopologyListPrimitiveRestartSupported()) || + SupportsPrimitiveRestart(input_assembly_topology) || + (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsPatchListPrimitiveRestartSupported())) + ? VK_TRUE + : VK_FALSE, }; const VkPipelineTessellationStateCreateInfo tessellation_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, @@ -672,15 +695,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pNext = nullptr, .flags = 0, .depthClampEnable = - static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), + static_cast<VkBool32>(dynamic.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), .rasterizerDiscardEnable = - static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), + static_cast<VkBool32>(dynamic.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), .polygonMode = MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)), .cullMode = static_cast<VkCullModeFlags>( dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), - .depthBiasEnable = key.state.depth_bias_enable, + .depthBiasEnable = (dynamic.depth_bias_enable != 0 ? VK_TRUE : VK_FALSE), .depthBiasConstantFactor = 0.0f, .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, @@ -782,13 +805,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .logicOpEnable = key.state.logic_op_enable != 0, - .logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()), + .logicOpEnable = dynamic.logic_op_enable != 0, + .logicOp = static_cast<VkLogicOp>(dynamic.logic_op.Value()), .attachmentCount = static_cast<u32>(cb_attachments.size()), .pAttachments = cb_attachments.data(), .blendConstants = {}, }; - static_vector<VkDynamicState, 19> dynamic_states{ + static_vector<VkDynamicState, 28> dynamic_states{ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, @@ -811,6 +834,32 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); } dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); + if (key.state.extended_dynamic_state_2) { + static constexpr std::array extended2{ + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); + } + if (key.state.extended_dynamic_state_2_extra) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); + } + if (key.state.extended_dynamic_state_3_blend) { + static constexpr std::array extended3{ + VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, + VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, + VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + } + if (key.state.extended_dynamic_state_3_enables) { + static constexpr std::array extended3{ + VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, + VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + } } const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, @@ -849,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } - pipeline = device.GetLogical().CreateGraphicsPipeline({ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = flags, - .stageCount = static_cast<u32>(shader_stages.size()), - .pStages = shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = &tessellation_ci, - .pViewportState = &viewport_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisample_ci, - .pDepthStencilState = &depth_stencil_ci, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = render_pass, - .subpass = 0, - .basePipelineHandle = nullptr, - .basePipelineIndex = 0, - }); + pipeline = device.GetLogical().CreateGraphicsPipeline( + { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = flags, + .stageCount = static_cast<u32>(shader_stages.size()), + .pStages = shader_stages.data(), + .pVertexInputState = &vertex_input_ci, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = &tessellation_ci, + .pViewportState = &viewport_ci, + .pRasterizationState = &rasterization_ci, + .pMultisampleState = &multisample_ci, + .pDepthStencilState = &depth_stencil_ci, + .pColorBlendState = &color_blend_ci, + .pDynamicState = &dynamic_state_ci, + .layout = *pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }, + *pipeline_cache); } void GraphicsPipeline::Validate() { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 1ed2967be..67c657d0e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -70,16 +70,14 @@ class GraphicsPipeline { static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; public: - explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, - TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, - const Device& device, DescriptorPool& descriptor_pool, - UpdateDescriptorQueue& update_descriptor_queue, - Common::ThreadWorker* worker_thread, - PipelineStatistics* pipeline_statistics, - RenderPassCache& render_pass_cache, - const GraphicsPipelineCacheKey& key, - std::array<vk::ShaderModule, NUM_STAGES> stages, - const std::array<const Shader::Info*, NUM_STAGES>& infos); + explicit GraphicsPipeline( + Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, + vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify, + const Device& device, DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* worker_thread, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages, + const std::array<const Shader::Info*, NUM_STAGES>& infos); GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; @@ -133,6 +131,7 @@ private: const Device& device; TextureCache& texture_cache; BufferCache& buffer_cache; + vk::PipelineCache& pipeline_cache; Scheduler& scheduler; UpdateDescriptorQueue& update_descriptor_queue; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e7262420c..7e69b11d8 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -54,7 +54,8 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 8; +constexpr u32 CACHE_VERSION = 10; +constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template <typename Container> auto MakeSpan(Container& container) { @@ -284,6 +285,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, + use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; @@ -329,6 +331,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .need_declared_frag_colors = false, .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, + .has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, @@ -341,6 +344,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), + .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), + .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), }; if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { @@ -351,9 +356,23 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); } + + dynamic_features = DynamicFeatures{ + .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(), + .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(), + .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(), + .has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(), + .has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(), + .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(), + }; } -PipelineCache::~PipelineCache() = default; +PipelineCache::~PipelineCache() { + if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); + } +} GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { MICROPROFILE_SCOPE(Vulkan_PipelineCache); @@ -362,8 +381,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), - device.IsExtVertexInputDynamicStateSupported()); + graphics_key.state.Refresh(*maxwell3d, dynamic_features); if (current_pipeline) { GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; @@ -410,6 +428,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } pipeline_cache_filename = base_dir / "vulkan.bin"; + if (use_vulkan_pipeline_cache) { + vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin"; + vulkan_pipeline_cache = + LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION); + } + struct { std::mutex mutex; size_t total{}; @@ -439,14 +463,21 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading }); ++state.total; }}; - const bool extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(); - const bool dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(); const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { GraphicsPipelineCacheKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - if ((key.state.extended_dynamic_state != 0) != extended_dynamic_state || - (key.state.dynamic_vertex_input != 0) != dynamic_vertex_input) { + if ((key.state.extended_dynamic_state != 0) != + dynamic_features.has_extended_dynamic_state || + (key.state.extended_dynamic_state_2 != 0) != + dynamic_features.has_extended_dynamic_state_2 || + (key.state.extended_dynamic_state_2_extra != 0) != + dynamic_features.has_extended_dynamic_state_2_extra || + (key.state.extended_dynamic_state_3_blend != 0) != + dynamic_features.has_extended_dynamic_state_3_blend || + (key.state.extended_dynamic_state_3_enables != 0) != + dynamic_features.has_extended_dynamic_state_3_enables || + (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { return; } workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { @@ -481,6 +512,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); + if (use_vulkan_pipeline_cache) { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); + } + if (state.statistics) { state.statistics->Report(); } @@ -601,10 +637,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( previous_stage = &program; } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache, - &shader_notify, device, descriptor_pool, - update_descriptor_queue, thread_worker, statistics, - render_pass_cache, key, std::move(modules), infos); + return std::make_unique<GraphicsPipeline>( + scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, + descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, + std::move(modules), infos); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); @@ -674,13 +710,108 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( spv_module.SetObjectNameEXT(name.c_str()); } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, - thread_worker, statistics, &shader_notify, - program.info, std::move(spv_module)); + return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool, + update_descriptor_queue, thread_worker, statistics, + &shader_notify, program.info, std::move(spv_module)); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); return nullptr; } +void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename, + const vk::PipelineCache& pipeline_cache, + u32 cache_version) try { + std::ofstream file(filename, std::ios::binary); + file.exceptions(std::ifstream::failbit); + if (!file.is_open()) { + LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + return; + } + file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()) + .write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version)); + + size_t cache_size = 0; + std::vector<char> cache_data; + if (pipeline_cache) { + pipeline_cache.Read(&cache_size, nullptr); + cache_data.resize(cache_size); + pipeline_cache.Read(&cache_size, cache_data.data()); + } + file.write(cache_data.data(), cache_size); + + LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}", + Common::FS::PathToUTF8String(filename)); + +} catch (const std::ios_base::failure& e) { + LOG_ERROR(Common_Filesystem, "{}", e.what()); + if (!Common::FS::RemoveFile(filename)) { + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + } +} + +vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename, + u32 expected_cache_version) { + const auto create_pipeline_cache = [this](size_t data_size, const void* data) { + VkPipelineCacheCreateInfo pipeline_cache_ci = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .initialDataSize = data_size, + .pInitialData = data}; + return device.GetLogical().CreatePipelineCache(pipeline_cache_ci); + }; + try { + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + return create_pipeline_cache(0, nullptr); + } + file.exceptions(std::ifstream::failbit); + const auto end{file.tellg()}; + file.seekg(0, std::ios::beg); + + std::array<char, 8> magic_number; + u32 cache_version; + file.read(magic_number.data(), magic_number.size()) + .read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version)); + if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) { + file.close(); + if (Common::FS::RemoveFile(filename)) { + if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { + LOG_ERROR(Common_Filesystem, "Invalid Vulkan driver pipeline cache file"); + } + if (cache_version != expected_cache_version) { + LOG_INFO(Common_Filesystem, "Deleting old Vulkan driver pipeline cache"); + } + } else { + LOG_ERROR(Common_Filesystem, + "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"", + Common::FS::PathToUTF8String(filename)); + } + return create_pipeline_cache(0, nullptr); + } + + static constexpr size_t header_size = magic_number.size() + sizeof(cache_version); + const size_t cache_size = static_cast<size_t>(end) - header_size; + std::vector<char> cache_data(cache_size); + file.read(cache_data.data(), cache_size); + + LOG_INFO(Render_Vulkan, + "Loaded Vulkan driver pipeline cache: ", Common::FS::PathToUTF8String(filename)); + + return create_pipeline_cache(cache_size, cache_data.data()); + + } catch (const std::ios_base::failure& e) { + LOG_ERROR(Common_Filesystem, "{}", e.what()); + if (!Common::FS::RemoveFile(filename)) { + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + } + + return create_pipeline_cache(0, nullptr); + } +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 61f9e9366..5171912d7 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -135,6 +135,12 @@ private: PipelineStatistics* statistics, bool build_in_parallel); + void SerializeVulkanPipelineCache(const std::filesystem::path& filename, + const vk::PipelineCache& pipeline_cache, u32 cache_version); + + vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, + u32 expected_cache_version); + const Device& device; Scheduler& scheduler; DescriptorPool& descriptor_pool; @@ -144,6 +150,7 @@ private: TextureCache& texture_cache; VideoCore::ShaderNotify& shader_notify; bool use_asynchronous_shaders{}; + bool use_vulkan_pipeline_cache{}; GraphicsPipelineCacheKey graphics_key{}; GraphicsPipeline* current_pipeline{}; @@ -158,8 +165,12 @@ private: std::filesystem::path pipeline_cache_filename; + std::filesystem::path vulkan_pipeline_cache_filename; + vk::PipelineCache vulkan_pipeline_cache; + Common::ThreadWorker workers; Common::ThreadWorker serialization_thread; + DynamicFeatures dynamic_features; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4b7126c30..719edbcfb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -138,12 +138,16 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, .first_index = is_indexed ? draw_state.index_buffer.first : 0, .is_indexed = is_indexed, }; + // 6 triangle vertices per quad, base vertex is part of the index + // See BindQuadIndexBuffer for more details if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { - // 6 triangle vertices per quad, base vertex is part of the index - // See BindQuadArrayIndexBuffer for more details params.num_vertices = (params.num_vertices / 4) * 6; params.base_vertex = 0; params.is_indexed = true; + } else if (draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { + params.num_vertices = (params.num_vertices - 2) / 2 * 6; + params.base_vertex = 0; + params.is_indexed = true; } return params; } @@ -176,11 +180,13 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra RasterizerVulkan::~RasterizerVulkan() = default; -void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { +template <typename Func> +void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { MICROPROFILE_SCOPE(Vulkan_Drawing); SCOPE_EXIT({ gpu.TickWork(); }); FlushWork(); + gpu_memory->FlushCaching(); query_cache.UpdateCounters(); @@ -197,26 +203,103 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { UpdateDynamicStates(); - const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); - const u32 num_instances{instance_count}; - const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; - scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { - if (draw_params.is_indexed) { - cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, - draw_params.first_index, draw_params.base_vertex, - draw_params.base_instance); - } else { - cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, - draw_params.base_vertex, draw_params.base_instance); + draw_func(); + + EndTransformFeedback(); +} + +void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { + PrepareDraw(is_indexed, [this, is_indexed, instance_count] { + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const u32 num_instances{instance_count}; + const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; + scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { + if (draw_params.is_indexed) { + cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, + draw_params.first_index, draw_params.base_vertex, + draw_params.base_instance); + } else { + cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, + draw_params.base_vertex, draw_params.base_instance); + } + }); + }); +} + +void RasterizerVulkan::DrawIndirect() { + const auto& params = maxwell3d->draw_manager->GetIndirectParams(); + buffer_cache.SetDrawIndirect(¶ms); + PrepareDraw(params.is_indexed, [this, ¶ms] { + const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer(); + const auto& buffer = indirect_buffer.first; + const auto& offset = indirect_buffer.second; + if (params.include_count) { + const auto count = buffer_cache.GetDrawIndirectCount(); + const auto& draw_buffer = count.first; + const auto& offset_base = count.second; + scheduler.Record([draw_buffer_obj = draw_buffer->Handle(), + buffer_obj = buffer->Handle(), offset_base, offset, + params](vk::CommandBuffer cmdbuf) { + if (params.is_indexed) { + cmdbuf.DrawIndexedIndirectCount( + buffer_obj, offset, draw_buffer_obj, offset_base, + static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride)); + } else { + cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base, + static_cast<u32>(params.max_draw_counts), + static_cast<u32>(params.stride)); + } + }); + return; } + scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) { + if (params.is_indexed) { + cmdbuf.DrawIndexedIndirect(buffer_obj, offset, + static_cast<u32>(params.max_draw_counts), + static_cast<u32>(params.stride)); + } else { + cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts), + static_cast<u32>(params.stride)); + } + }); }); - EndTransformFeedback(); + buffer_cache.SetDrawIndirect(nullptr); +} + +void RasterizerVulkan::DrawTexture() { + MICROPROFILE_SCOPE(Vulkan_Drawing); + + SCOPE_EXIT({ gpu.TickWork(); }); + FlushWork(); + + query_cache.UpdateCounters(); + + texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.UpdateRenderTargets(false); + + UpdateDynamicStates(); + + const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); + const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); + const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); + Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0), + .y = static_cast<s32>(draw_texture_state.dst_y0)}, + Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1), + .y = static_cast<s32>(draw_texture_state.dst_y1)}}; + Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0), + .y = static_cast<s32>(draw_texture_state.src_y0)}, + Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1), + .y = static_cast<s32>(draw_texture_state.src_y1)}}; + blit_image.BlitColor(texture_cache.GetFramebuffer(), texture.RenderTarget(), + texture.ImageHandle(), sampler->Handle(), dst_region, src_region, + texture.size); } void RasterizerVulkan::Clear(u32 layer_count) { MICROPROFILE_SCOPE(Vulkan_Clearing); FlushWork(); + gpu_memory->FlushCaching(); query_cache.UpdateCounters(); @@ -311,7 +394,15 @@ void RasterizerVulkan::Clear(u32 layer_count) { cmdbuf.ClearAttachments(attachment, clear_rect); }); } else { - UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); + u8 color_mask = static_cast<u8>(regs.clear_surface.R | regs.clear_surface.G << 1 | + regs.clear_surface.B << 2 | regs.clear_surface.A << 3); + Region2D dst_region = { + Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, + Offset2D{.x = clear_rect.rect.offset.x + + static_cast<s32>(clear_rect.rect.extent.width), + .y = clear_rect.rect.offset.y + + static_cast<s32>(clear_rect.rect.extent.height)}}; + blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region); } } @@ -341,6 +432,7 @@ void RasterizerVulkan::Clear(u32 layer_count) { void RasterizerVulkan::DispatchCompute() { FlushWork(); + gpu_memory->FlushCaching(); ComputePipeline* const pipeline{pipeline_cache.CurrentComputePipeline()}; if (!pipeline) { @@ -375,44 +467,79 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in void RasterizerVulkan::FlushAll() {} -void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { +void RasterizerVulkan::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { if (addr == 0 || size == 0) { return; } - { + if (True(which & VideoCommon::CacheType::TextureCache)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.DownloadMemory(addr, size); } - { + if ((True(which & VideoCommon::CacheType::BufferCache))) { std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.DownloadMemory(addr, size); } - query_cache.FlushRegion(addr, size); + if ((True(which & VideoCommon::CacheType::QueryCache))) { + query_cache.FlushRegion(addr, size); + } } -bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { - std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; +bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { + if ((True(which & VideoCommon::CacheType::BufferCache))) { + std::scoped_lock lock{buffer_cache.mutex}; + if (buffer_cache.IsRegionGpuModified(addr, size)) { + return true; + } + } if (!Settings::IsGPULevelHigh()) { - return buffer_cache.IsRegionGpuModified(addr, size); + return false; } - return texture_cache.IsRegionGpuModified(addr, size) || - buffer_cache.IsRegionGpuModified(addr, size); + if (True(which & VideoCommon::CacheType::TextureCache)) { + std::scoped_lock lock{texture_cache.mutex}; + return texture_cache.IsRegionGpuModified(addr, size); + } + return false; } -void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { +void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { if (addr == 0 || size == 0) { return; } - { + if (True(which & VideoCommon::CacheType::TextureCache)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(addr, size); } - { + if ((True(which & VideoCommon::CacheType::BufferCache))) { std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.WriteMemory(addr, size); } - pipeline_cache.InvalidateRegion(addr, size); - query_cache.InvalidateRegion(addr, size); + if ((True(which & VideoCommon::CacheType::QueryCache))) { + query_cache.InvalidateRegion(addr, size); + } + if ((True(which & VideoCommon::CacheType::ShaderCache))) { + pipeline_cache.InvalidateRegion(addr, size); + } +} + +void RasterizerVulkan::InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) { + { + std::scoped_lock lock{texture_cache.mutex}; + for (const auto& [addr, size] : sequences) { + texture_cache.WriteMemory(addr, size); + } + } + { + std::scoped_lock lock{buffer_cache.mutex}; + for (const auto& [addr, size] : sequences) { + buffer_cache.WriteMemory(addr, size); + } + } + { + for (const auto& [addr, size] : sequences) { + query_cache.InvalidateRegion(addr, size); + pipeline_cache.InvalidateRegion(addr, size); + } + } } void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { @@ -477,11 +604,12 @@ void RasterizerVulkan::ReleaseFences() { fence_manager.WaitPendingFences(); } -void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { +void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which) { if (Settings::IsGPULevelExtreme()) { - FlushRegion(addr, size); + FlushRegion(addr, size, which); } - InvalidateRegion(addr, size); + InvalidateRegion(addr, size, which); } void RasterizerVulkan::WaitForIdle() { @@ -537,6 +665,22 @@ void RasterizerVulkan::TickFrame() { } } +bool RasterizerVulkan::AccelerateConditionalRendering() { + gpu_memory->FlushCaching(); + if (Settings::IsGPULevelHigh()) { + // TODO(Blinkhawk): Reimplement Host conditional rendering. + return false; + } + // Medium / Low Hack: stub any checks on queries writen into the buffer cache. + const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()}; + Maxwell::ReportSemaphore::Compare cmp; + if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp), + VideoCommon::CacheType::BufferCache)) { + return true; + } + return false; +} + bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { @@ -557,7 +701,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::unique_lock<std::mutex> lock{buffer_cache.mutex}; + std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } @@ -635,16 +779,35 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateLineWidth(regs); if (device.IsExtExtendedDynamicStateSupported()) { UpdateCullMode(regs); - UpdateDepthBoundsTestEnable(regs); - UpdateDepthTestEnable(regs); - UpdateDepthWriteEnable(regs); UpdateDepthCompareOp(regs); UpdateFrontFace(regs); UpdateStencilOp(regs); - UpdateStencilTestEnable(regs); + if (device.IsExtVertexInputDynamicStateSupported()) { UpdateVertexInput(regs); } + + if (state_tracker.TouchStateEnable()) { + UpdateDepthBoundsTestEnable(regs); + UpdateDepthTestEnable(regs); + UpdateDepthWriteEnable(regs); + UpdateStencilTestEnable(regs); + if (device.IsExtExtendedDynamicState2Supported()) { + UpdatePrimitiveRestartEnable(regs); + UpdateRasterizerDiscardEnable(regs); + UpdateDepthBiasEnable(regs); + } + if (device.IsExtExtendedDynamicState3EnablesSupported()) { + UpdateLogicOpEnable(regs); + UpdateDepthClampEnable(regs); + } + } + if (device.IsExtExtendedDynamicState2ExtrasSupported()) { + UpdateLogicOp(regs); + } + if (device.IsExtExtendedDynamicState3Supported()) { + UpdateBlending(regs); + } } } @@ -785,32 +948,92 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) if (!state_tracker.TouchStencilProperties()) { return; } - if (regs.stencil_two_side_enable) { - // Separate values per face - scheduler.Record( - [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask, - front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref, - back_write_mask = regs.stencil_back_mask, - back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { + bool update_references = state_tracker.TouchStencilReference(); + bool update_write_mask = state_tracker.TouchStencilWriteMask(); + bool update_compare_masks = state_tracker.TouchStencilCompare(); + if (state_tracker.TouchStencilSide(regs.stencil_two_side_enable != 0)) { + update_references = true; + update_write_mask = true; + update_compare_masks = true; + } + if (update_references) { + [&]() { + if (regs.stencil_two_side_enable) { + if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) && + !state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) { + return; + } + } else { + if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) { + return; + } + } + scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref, + two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { + const bool set_back = two_sided && front_ref != back_ref; // Front face - cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref); - cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask); - cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT, front_test_mask); - - // Back face - cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref); - cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask); - cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask); + cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT + : VK_STENCIL_FACE_FRONT_AND_BACK, + front_ref); + if (set_back) { + cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref); + } }); - } else { - // Front face defines both faces - scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask, - test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { - cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); - cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); - cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); - }); + }(); + } + if (update_write_mask) { + [&]() { + if (regs.stencil_two_side_enable) { + if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) && + !state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) { + return; + } + } else { + if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) { + return; + } + } + scheduler.Record([front_write_mask = regs.stencil_front_mask, + back_write_mask = regs.stencil_back_mask, + two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { + const bool set_back = two_sided && front_write_mask != back_write_mask; + // Front face + cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT + : VK_STENCIL_FACE_FRONT_AND_BACK, + front_write_mask); + if (set_back) { + cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask); + } + }); + }(); + } + if (update_compare_masks) { + [&]() { + if (regs.stencil_two_side_enable) { + if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) && + !state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) { + return; + } + } else { + if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) { + return; + } + } + scheduler.Record([front_test_mask = regs.stencil_front_func_mask, + back_test_mask = regs.stencil_back_func_mask, + two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { + const bool set_back = two_sided && front_test_mask != back_test_mask; + // Front face + cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT + : VK_STENCIL_FACE_FRONT_AND_BACK, + front_test_mask); + if (set_back) { + cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask); + } + }); + }(); } + state_tracker.ClearStencilReset(); } void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { @@ -864,6 +1087,82 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r }); } +void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchPrimitiveRestartEnable()) { + return; + } + scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { + cmdbuf.SetPrimitiveRestartEnableEXT(enable); + }); +} + +void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchRasterizerDiscardEnable()) { + return; + } + scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) { + cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0); + }); +} + +void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchDepthBiasEnable()) { + return; + } + constexpr size_t POINT = 0; + constexpr size_t LINE = 1; + constexpr size_t POLYGON = 2; + static constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { + POINT, // Points + LINE, // Lines + LINE, // LineLoop + LINE, // LineStrip + POLYGON, // Triangles + POLYGON, // TriangleStrip + POLYGON, // TriangleFan + POLYGON, // Quads + POLYGON, // QuadStrip + POLYGON, // Polygon + LINE, // LinesAdjacency + LINE, // LineStripAdjacency + POLYGON, // TrianglesAdjacency + POLYGON, // TriangleStripAdjacency + POLYGON, // Patches + }; + const std::array enabled_lut{ + regs.polygon_offset_point_enable, + regs.polygon_offset_line_enable, + regs.polygon_offset_fill_enable, + }; + const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology); + const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]]; + scheduler.Record( + [enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); }); +} + +void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchLogicOpEnable()) { + return; + } + scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) { + cmdbuf.SetLogicOpEnableEXT(enable != 0); + }); +} + +void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchDepthClampEnable()) { + return; + } + bool is_enabled = !(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); + scheduler.Record( + [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); +} + void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchDepthCompareOp()) { return; @@ -921,6 +1220,78 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { } } +void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchLogicOp()) { + return; + } + const auto op_value = static_cast<u32>(regs.logic_op.op); + auto op = op_value >= 0x1500 && op_value < 0x1510 ? static_cast<VkLogicOp>(op_value - 0x1500) + : VK_LOGIC_OP_NO_OP; + scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); }); +} + +void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchBlending()) { + return; + } + + if (state_tracker.TouchColorMask()) { + std::array<VkColorComponentFlags, Maxwell::NumRenderTargets> setup_masks{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; + auto& current = setup_masks[index]; + if (mask.R) { + current |= VK_COLOR_COMPONENT_R_BIT; + } + if (mask.G) { + current |= VK_COLOR_COMPONENT_G_BIT; + } + if (mask.B) { + current |= VK_COLOR_COMPONENT_B_BIT; + } + if (mask.A) { + current |= VK_COLOR_COMPONENT_A_BIT; + } + } + scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteMaskEXT(0, setup_masks); + }); + } + + if (state_tracker.TouchBlendEnable()) { + std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{}; + std::ranges::transform( + regs.blend.enable, setup_enables.begin(), + [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; }); + scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorBlendEnableEXT(0, setup_enables); + }); + } + + if (state_tracker.TouchBlendEquations()) { + std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + const auto blend_setup = [&]<typename T>(const T& guest_blend) { + auto& host_blend = setup_blends[index]; + host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source); + host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest); + host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op); + host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source); + host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest); + host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op); + }; + if (!regs.blend_per_target_enabled) { + blend_setup(regs.blend); + continue; + } + blend_setup(regs.blend_per_target[index]); + } + scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorBlendEquationEXT(0, setup_blends); + }); + } +} + void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchStencilTestEnable()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index ee483cfd9..a0508b57c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -65,6 +65,8 @@ public: ~RasterizerVulkan() override; void Draw(bool is_indexed, u32 instance_count) override; + void DrawIndirect() override; + void DrawTexture() override; void Clear(u32 layer_count) override; void DispatchCompute() override; void ResetCounter(VideoCore::QueryType type) override; @@ -72,9 +74,13 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void FlushAll() override; - void FlushRegion(VAddr addr, u64 size) override; - bool MustFlushRegion(VAddr addr, u64 size) override; - void InvalidateRegion(VAddr addr, u64 size) override; + void FlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + bool MustFlushRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void InvalidateRegion(VAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; void OnCPUWrite(VAddr addr, u64 size) override; void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; @@ -84,12 +90,14 @@ public: void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; - void FlushAndInvalidateRegion(VAddr addr, u64 size) override; + void FlushAndInvalidateRegion( + VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void WaitForIdle() override; void FragmentBarrier() override; void TiledCacheBarrier() override; void FlushCommands() override; void TickFrame() override; + bool AccelerateConditionalRendering() override; bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; @@ -114,6 +122,9 @@ private: static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); + template <typename Func> + void PrepareDraw(bool is_indexed, Func&&); + void FlushWork(); void UpdateDynamicStates(); @@ -135,9 +146,16 @@ private: void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index c2e53a5d5..e03685af1 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -213,6 +213,11 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s .signalSemaphoreCount = num_signal_semaphores, .pSignalSemaphores = signal_semaphores.data(), }; + + if (on_submit) { + on_submit(); + } + switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) { case VK_SUCCESS: break; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 3858c506c..bd4cb0f7e 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -5,6 +5,7 @@ #include <condition_variable> #include <cstddef> +#include <functional> #include <memory> #include <thread> #include <utility> @@ -66,6 +67,11 @@ public: query_cache = &query_cache_; } + // Registers a callback to perform on queue submission. + void RegisterOnSubmit(std::function<void()>&& func) { + on_submit = std::move(func); + } + /// Send work to a separate thread. template <typename T> void Record(T&& command) { @@ -216,6 +222,7 @@ private: vk::CommandBuffer current_cmdbuf; std::unique_ptr<CommandChunk> chunk; + std::function<void()> on_submit; State state; diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 06f68d09a..74ca77216 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include <algorithm> #include <utility> @@ -94,7 +94,8 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem .flags = 0, .size = STREAM_BUFFER_SIZE, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -142,11 +143,23 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem StagingBufferPool::~StagingBufferPool() = default; -StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { - if (usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { +StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) { + if (!deferred && usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { return GetStreamBuffer(size); } - return GetStagingBuffer(size, usage); + return GetStagingBuffer(size, usage, deferred); +} + +void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) { + auto& entries = GetCache(ref.usage)[ref.log2_level].entries; + const auto is_this_one = [&ref](const StagingBuffer& entry) { + return entry.index == ref.index; + }; + auto it = std::find_if(entries.begin(), entries.end(), is_this_one); + ASSERT(it != entries.end()); + ASSERT(it->deferred); + it->tick = scheduler.CurrentTick(); + it->deferred = false; } void StagingBufferPool::TickFrame() { @@ -187,6 +200,9 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { .buffer = *stream_buffer, .offset = static_cast<VkDeviceSize>(offset), .mapped_span = std::span<u8>(stream_pointer + offset, size), + .usage{}, + .log2_level{}, + .index{}, }; } @@ -196,19 +212,21 @@ bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; }); }; -StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage) { - if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { +StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage, + bool deferred) { + if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) { return *ref; } - return CreateStagingBuffer(size, usage); + return CreateStagingBuffer(size, usage, deferred); } std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, - MemoryUsage usage) { + MemoryUsage usage, + bool deferred) { StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; const auto is_free = [this](const StagingBuffer& entry) { - return scheduler.IsFree(entry.tick); + return !entry.deferred && scheduler.IsFree(entry.tick); }; auto& entries = cache_level.entries; const auto hint_it = entries.begin() + cache_level.iterate_index; @@ -220,11 +238,14 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s } } cache_level.iterate_index = std::distance(entries.begin(), it) + 1; - it->tick = scheduler.CurrentTick(); + it->tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick(); + ASSERT(!it->deferred); + it->deferred = deferred; return it->Ref(); } -StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { +StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, + bool deferred) { const u32 log2 = Common::Log2Ceil64(size); vk::Buffer buffer = device.GetLogical().CreateBuffer({ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -233,7 +254,8 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage .size = 1ULL << log2, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -249,7 +271,11 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage .buffer = std::move(buffer), .commit = std::move(commit), .mapped_span = mapped_span, - .tick = scheduler.CurrentTick(), + .usage = usage, + .log2_level = log2, + .index = unique_ids++, + .tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick(), + .deferred = deferred, }); return entry.Ref(); } diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 91dc84da8..4fd15f11a 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -20,6 +20,9 @@ struct StagingBufferRef { VkBuffer buffer; VkDeviceSize offset; std::span<u8> mapped_span; + MemoryUsage usage; + u32 log2_level; + u64 index; }; class StagingBufferPool { @@ -30,7 +33,8 @@ public: Scheduler& scheduler); ~StagingBufferPool(); - StagingBufferRef Request(size_t size, MemoryUsage usage); + StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); + void FreeDeferred(StagingBufferRef& ref); void TickFrame(); @@ -44,13 +48,20 @@ private: vk::Buffer buffer; MemoryCommit commit; std::span<u8> mapped_span; + MemoryUsage usage; + u32 log2_level; + u64 index; u64 tick = 0; + bool deferred{}; StagingBufferRef Ref() const noexcept { return { .buffer = *buffer, .offset = 0, .mapped_span = mapped_span, + .usage = usage, + .log2_level = log2_level, + .index = index, }; } }; @@ -68,11 +79,12 @@ private: bool AreRegionsActive(size_t region_begin, size_t region_end) const; - StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage); + StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false); - std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); + std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage, + bool deferred); - StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); + StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred); StagingBuffersCache& GetCache(MemoryUsage usage); @@ -99,6 +111,7 @@ private: size_t current_delete_level = 0; u64 buffer_index = 0; + u64 unique_ids{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index edb41b171..d56558a83 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -27,10 +27,37 @@ using Flags = Maxwell3D::DirtyState::Flags; Flags MakeInvalidationFlags() { static constexpr int INVALIDATION_FLAGS[]{ - Viewports, Scissors, DepthBias, BlendConstants, DepthBounds, - StencilProperties, LineWidth, CullMode, DepthBoundsEnable, DepthTestEnable, - DepthWriteEnable, DepthCompareOp, FrontFace, StencilOp, StencilTestEnable, - VertexBuffers, VertexInput, + Viewports, + Scissors, + DepthBias, + BlendConstants, + DepthBounds, + StencilProperties, + StencilReference, + StencilWriteMask, + StencilCompare, + LineWidth, + CullMode, + DepthBoundsEnable, + DepthTestEnable, + DepthWriteEnable, + DepthCompareOp, + FrontFace, + StencilOp, + StencilTestEnable, + VertexBuffers, + VertexInput, + StateEnable, + PrimitiveRestartEnable, + RasterizerDiscardEnable, + DepthBiasEnable, + LogicOpEnable, + DepthClampEnable, + LogicOp, + Blending, + ColorMask, + BlendEquations, + BlendEnable, }; Flags flags{}; for (const int flag : INVALIDATION_FLAGS) { @@ -75,14 +102,17 @@ void SetupDirtyDepthBounds(Tables& tables) { } void SetupDirtyStencilProperties(Tables& tables) { - auto& table = tables[0]; - table[OFF(stencil_two_side_enable)] = StencilProperties; - table[OFF(stencil_front_ref)] = StencilProperties; - table[OFF(stencil_front_mask)] = StencilProperties; - table[OFF(stencil_front_func_mask)] = StencilProperties; - table[OFF(stencil_back_ref)] = StencilProperties; - table[OFF(stencil_back_mask)] = StencilProperties; - table[OFF(stencil_back_func_mask)] = StencilProperties; + const auto setup = [&](size_t position, u8 flag) { + tables[0][position] = flag; + tables[1][position] = StencilProperties; + }; + tables[0][OFF(stencil_two_side_enable)] = StencilProperties; + setup(OFF(stencil_front_ref), StencilReference); + setup(OFF(stencil_front_mask), StencilWriteMask); + setup(OFF(stencil_front_func_mask), StencilCompare); + setup(OFF(stencil_back_ref), StencilReference); + setup(OFF(stencil_back_mask), StencilWriteMask); + setup(OFF(stencil_back_func_mask), StencilCompare); } void SetupDirtyLineWidth(Tables& tables) { @@ -96,16 +126,22 @@ void SetupDirtyCullMode(Tables& tables) { table[OFF(gl_cull_test_enabled)] = CullMode; } -void SetupDirtyDepthBoundsEnable(Tables& tables) { - tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; -} - -void SetupDirtyDepthTestEnable(Tables& tables) { - tables[0][OFF(depth_test_enable)] = DepthTestEnable; -} - -void SetupDirtyDepthWriteEnable(Tables& tables) { - tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; +void SetupDirtyStateEnable(Tables& tables) { + const auto setup = [&](size_t position, u8 flag) { + tables[0][position] = flag; + tables[1][position] = StateEnable; + }; + setup(OFF(depth_bounds_enable), DepthBoundsEnable); + setup(OFF(depth_test_enable), DepthTestEnable); + setup(OFF(depth_write_enabled), DepthWriteEnable); + setup(OFF(stencil_enable), StencilTestEnable); + setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable); + setup(OFF(rasterize_enable), RasterizerDiscardEnable); + setup(OFF(polygon_offset_point_enable), DepthBiasEnable); + setup(OFF(polygon_offset_line_enable), DepthBiasEnable); + setup(OFF(polygon_offset_fill_enable), DepthBiasEnable); + setup(OFF(logic_op.enable), LogicOpEnable); + setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable); } void SetupDirtyDepthCompareOp(Tables& tables) { @@ -133,16 +169,22 @@ void SetupDirtyStencilOp(Tables& tables) { tables[1][OFF(stencil_two_side_enable)] = StencilOp; } -void SetupDirtyStencilTestEnable(Tables& tables) { - tables[0][OFF(stencil_enable)] = StencilTestEnable; -} - void SetupDirtyBlending(Tables& tables) { tables[0][OFF(color_mask_common)] = Blending; + tables[1][OFF(color_mask_common)] = ColorMask; tables[0][OFF(blend_per_target_enabled)] = Blending; + tables[1][OFF(blend_per_target_enabled)] = BlendEquations; FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); + FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMask); FillBlock(tables[0], OFF(blend), NUM(blend), Blending); + FillBlock(tables[1], OFF(blend), NUM(blend), BlendEquations); + FillBlock(tables[1], OFF(blend.enable), NUM(blend.enable), BlendEnable); FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); + FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendEquations); +} + +void SetupDirtySpecialOps(Tables& tables) { + tables[0][OFF(logic_op.op)] = LogicOp; } void SetupDirtyViewportSwizzles(Tables& tables) { @@ -185,17 +227,15 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyStencilProperties(tables); SetupDirtyLineWidth(tables); SetupDirtyCullMode(tables); - SetupDirtyDepthBoundsEnable(tables); - SetupDirtyDepthTestEnable(tables); - SetupDirtyDepthWriteEnable(tables); + SetupDirtyStateEnable(tables); SetupDirtyDepthCompareOp(tables); SetupDirtyFrontFace(tables); SetupDirtyStencilOp(tables); - SetupDirtyStencilTestEnable(tables); SetupDirtyBlending(tables); SetupDirtyViewportSwizzles(tables); SetupDirtyVertexAttributes(tables); SetupDirtyVertexBindings(tables); + SetupDirtySpecialOps(tables); } void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { @@ -204,6 +244,8 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { void StateTracker::InvalidateState() { flags->set(); + current_topology = INVALID_TOPOLOGY; + stencil_reset = true; } StateTracker::StateTracker() diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 2296dea60..8010ad26c 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -35,6 +35,9 @@ enum : u8 { BlendConstants, DepthBounds, StencilProperties, + StencilReference, + StencilWriteMask, + StencilCompare, LineWidth, CullMode, @@ -45,8 +48,18 @@ enum : u8 { FrontFace, StencilOp, StencilTestEnable, + PrimitiveRestartEnable, + RasterizerDiscardEnable, + DepthBiasEnable, + StateEnable, + LogicOp, + LogicOpEnable, + DepthClampEnable, Blending, + BlendEnable, + BlendEquations, + ColorMask, ViewportSwizzles, Last, @@ -64,6 +77,7 @@ public: void InvalidateCommandBufferState() { (*flags) |= invalidation_flags; current_topology = INVALID_TOPOLOGY; + stencil_reset = true; } void InvalidateViewports() { @@ -103,6 +117,57 @@ public: return Exchange(Dirty::StencilProperties, false); } + bool TouchStencilReference() { + return Exchange(Dirty::StencilReference, false); + } + + bool TouchStencilWriteMask() { + return Exchange(Dirty::StencilWriteMask, false); + } + + bool TouchStencilCompare() { + return Exchange(Dirty::StencilCompare, false); + } + + template <typename T> + bool ExchangeCheck(T& old_value, T new_value) { + bool result = old_value != new_value; + old_value = new_value; + return result; + } + + bool TouchStencilSide(bool two_sided_stencil_new) { + return ExchangeCheck(two_sided_stencil, two_sided_stencil_new) || stencil_reset; + } + + bool CheckStencilReferenceFront(u32 new_value) { + return ExchangeCheck(front.ref, new_value) || stencil_reset; + } + + bool CheckStencilReferenceBack(u32 new_value) { + return ExchangeCheck(back.ref, new_value) || stencil_reset; + } + + bool CheckStencilWriteMaskFront(u32 new_value) { + return ExchangeCheck(front.write_mask, new_value) || stencil_reset; + } + + bool CheckStencilWriteMaskBack(u32 new_value) { + return ExchangeCheck(back.write_mask, new_value) || stencil_reset; + } + + bool CheckStencilCompareMaskFront(u32 new_value) { + return ExchangeCheck(front.compare_mask, new_value) || stencil_reset; + } + + bool CheckStencilCompareMaskBack(u32 new_value) { + return ExchangeCheck(back.compare_mask, new_value) || stencil_reset; + } + + void ClearStencilReset() { + stencil_reset = false; + } + bool TouchLineWidth() const { return Exchange(Dirty::LineWidth, false); } @@ -111,6 +176,10 @@ public: return Exchange(Dirty::CullMode, false); } + bool TouchStateEnable() { + return Exchange(Dirty::StateEnable, false); + } + bool TouchDepthBoundsTestEnable() { return Exchange(Dirty::DepthBoundsEnable, false); } @@ -123,6 +192,26 @@ public: return Exchange(Dirty::DepthWriteEnable, false); } + bool TouchPrimitiveRestartEnable() { + return Exchange(Dirty::PrimitiveRestartEnable, false); + } + + bool TouchRasterizerDiscardEnable() { + return Exchange(Dirty::RasterizerDiscardEnable, false); + } + + bool TouchDepthBiasEnable() { + return Exchange(Dirty::DepthBiasEnable, false); + } + + bool TouchLogicOpEnable() { + return Exchange(Dirty::LogicOpEnable, false); + } + + bool TouchDepthClampEnable() { + return Exchange(Dirty::DepthClampEnable, false); + } + bool TouchDepthCompareOp() { return Exchange(Dirty::DepthCompareOp, false); } @@ -135,10 +224,30 @@ public: return Exchange(Dirty::StencilOp, false); } + bool TouchBlending() { + return Exchange(Dirty::Blending, false); + } + + bool TouchBlendEnable() { + return Exchange(Dirty::BlendEnable, false); + } + + bool TouchBlendEquations() { + return Exchange(Dirty::BlendEquations, false); + } + + bool TouchColorMask() { + return Exchange(Dirty::ColorMask, false); + } + bool TouchStencilTestEnable() { return Exchange(Dirty::StencilTestEnable, false); } + bool TouchLogicOp() { + return Exchange(Dirty::LogicOp, false); + } + bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { const bool has_changed = current_topology != new_topology; current_topology = new_topology; @@ -160,10 +269,20 @@ private: return is_dirty; } + struct StencilProperties { + u32 ref = 0; + u32 write_mask = 0; + u32 compare_mask = 0; + }; + Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; + bool two_sided_stencil = false; + StencilProperties front{}; + StencilProperties back{}; + bool stencil_reset = false; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index a65bbeb1c..d39372ec4 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -812,8 +812,12 @@ StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) { return staging_buffer_pool.Request(size, MemoryUsage::Upload); } -StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) { - return staging_buffer_pool.Request(size, MemoryUsage::Download); +StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { + return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred); +} + +void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) { + staging_buffer_pool.FreeDeferred(ref); } bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 7ec0df134..1f27a3589 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -51,7 +51,9 @@ public: StagingBufferRef UploadStagingBuffer(size_t size); - StagingBufferRef DownloadStagingBuffer(size_t size); + StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); + + void FreeDeferredStagingBuffer(StagingBufferRef& ref); void TickFrame(); @@ -347,6 +349,7 @@ struct TextureCacheParams { static constexpr bool FRAMEBUFFER_BLITS = false; static constexpr bool HAS_EMULATED_COPIES = false; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; + static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; using Runtime = Vulkan::TextureCacheRuntime; using Image = Vulkan::Image; @@ -354,6 +357,7 @@ struct TextureCacheParams { using ImageView = Vulkan::ImageView; using Sampler = Vulkan::Sampler; using Framebuffer = Vulkan::Framebuffer; + using AsyncBuffer = Vulkan::StagingBufferRef; }; using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp new file mode 100644 index 000000000..c42594149 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/literals.h" +#include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.h" +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/renderer_vulkan/vk_turbo_mode.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +using namespace Common::Literals; + +TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld) + : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} { + { + std::scoped_lock lk{m_submission_lock}; + m_submission_time = std::chrono::steady_clock::now(); + } + m_thread = std::jthread([&](auto stop_token) { Run(stop_token); }); +} + +TurboMode::~TurboMode() = default; + +void TurboMode::QueueSubmitted() { + std::scoped_lock lk{m_submission_lock}; + m_submission_time = std::chrono::steady_clock::now(); + m_submission_cv.notify_one(); +} + +void TurboMode::Run(std::stop_token stop_token) { + auto& dld = m_device.GetLogical(); + + // Allocate buffer. 2MiB should be sufficient. + auto buffer = dld.CreateBuffer(VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = 2_MiB, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }); + + // Commit some device local memory for the buffer. + auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal); + + // Create the descriptor pool to contain our descriptor. + constexpr VkDescriptorPoolSize pool_size{ + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + }; + + auto descriptor_pool = dld.CreateDescriptorPool(VkDescriptorPoolCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + }); + + // Create the descriptor set layout from the pool. + constexpr VkDescriptorSetLayoutBinding layout_binding{ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }; + + auto descriptor_set_layout = dld.CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .bindingCount = 1, + .pBindings = &layout_binding, + }); + + // Actually create the descriptor set. + auto descriptor_set = descriptor_pool.Allocate(VkDescriptorSetAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = *descriptor_pool, + .descriptorSetCount = 1, + .pSetLayouts = descriptor_set_layout.address(), + }); + + // Create the shader. + auto shader = BuildShader(m_device, VULKAN_TURBO_MODE_COMP_SPV); + + // Create the pipeline layout. + auto pipeline_layout = dld.CreatePipelineLayout(VkPipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = descriptor_set_layout.address(), + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, + }); + + // Actually create the pipeline. + const VkPipelineShaderStageCreateInfo shader_stage{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = *shader, + .pName = "main", + .pSpecializationInfo = nullptr, + }; + + auto pipeline = dld.CreateComputePipeline(VkComputePipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = shader_stage, + .layout = *pipeline_layout, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + }); + + // Create a fence to wait on. + auto fence = dld.CreateFence(VkFenceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + }); + + // Create a command pool to allocate a command buffer from. + auto command_pool = dld.CreateCommandPool(VkCommandPoolCreateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = m_device.GetGraphicsFamily(), + }); + + // Create a single command buffer. + auto cmdbufs = command_pool.Allocate(1, VK_COMMAND_BUFFER_LEVEL_PRIMARY); + auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()}; + + while (!stop_token.stop_requested()) { + // Reset the fence. + fence.Reset(); + + // Update descriptor set. + const VkDescriptorBufferInfo buffer_info{ + .buffer = *buffer, + .offset = 0, + .range = VK_WHOLE_SIZE, + }; + + const VkWriteDescriptorSet buffer_write{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = descriptor_set[0], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pImageInfo = nullptr, + .pBufferInfo = &buffer_info, + .pTexelBufferView = nullptr, + }; + + dld.UpdateDescriptorSets(std::array{buffer_write}, {}); + + // Set up the command buffer. + cmdbuf.Begin(VkCommandBufferBeginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }); + + // Clear the buffer. + cmdbuf.FillBuffer(*buffer, 0, VK_WHOLE_SIZE, 0); + + // Bind descriptor set. + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, + descriptor_set, {}); + + // Bind the pipeline. + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + + // Dispatch. + cmdbuf.Dispatch(64, 64, 1); + + // Finish. + cmdbuf.End(); + + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = cmdbuf.address(), + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + + m_device.GetGraphicsQueue().Submit(std::array{submit_info}, *fence); + + // Wait for completion. + fence.Wait(); + + // Wait for the next graphics queue submission if necessary. + std::unique_lock lk{m_submission_lock}; + Common::CondvarWait(m_submission_cv, lk, stop_token, [this] { + return (std::chrono::steady_clock::now() - m_submission_time) <= + std::chrono::milliseconds{100}; + }); + } +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h new file mode 100644 index 000000000..99b5ac50b --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <chrono> +#include <mutex> + +#include "common/polyfill_thread.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" + +namespace Vulkan { + +class TurboMode { +public: + explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld); + ~TurboMode(); + + void QueueSubmitted(); + +private: + void Run(std::stop_token stop_token); + + Device m_device; + MemoryAllocator m_allocator; + std::mutex m_submission_lock; + std::condition_variable_any m_submission_cv; + std::chrono::time_point<std::chrono::steady_clock> m_submission_time{}; + + std::jthread m_thread; +}; + +} // namespace Vulkan diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 958810747..574760f80 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -202,12 +202,15 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { const u64 num_texture_types{static_cast<u64>(texture_types.size())}; const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; + const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())}; file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) .write(reinterpret_cast<const char*>(&num_texture_pixel_formats), sizeof(num_texture_pixel_formats)) .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) + .write(reinterpret_cast<const char*>(&num_cbuf_replacement_values), + sizeof(num_cbuf_replacement_values)) .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) @@ -229,6 +232,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { file.write(reinterpret_cast<const char*>(&key), sizeof(key)) .write(reinterpret_cast<const char*>(&type), sizeof(type)); } + for (const auto& [key, type] : cbuf_replacements) { + file.write(reinterpret_cast<const char*>(&key), sizeof(key)) + .write(reinterpret_cast<const char*>(&type), sizeof(type)); + } if (stage == Shader::Stage::Compute) { file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size)) .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size)); @@ -318,6 +325,9 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, ASSERT(local_size <= std::numeric_limits<u32>::max()); local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size; texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; + is_propietary_driver = texture_bound == 2; + has_hle_engine_state = + maxwell3d->engine_state == Tegra::Engines::Maxwell3D::EngineHint::OnHLEMacro; } u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { @@ -331,6 +341,32 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { return value; } +std::optional<Shader::ReplaceConstant> GraphicsEnvironment::GetReplaceConstBuffer(u32 bank, + u32 offset) { + if (!has_hle_engine_state) { + return std::nullopt; + } + const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset); + auto it = maxwell3d->replace_table.find(key); + if (it == maxwell3d->replace_table.end()) { + return std::nullopt; + } + const auto converted_value = [](Tegra::Engines::Maxwell3D::HLEReplacementAttributeType name) { + switch (name) { + case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseVertex: + return Shader::ReplaceConstant::BaseVertex; + case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseInstance: + return Shader::ReplaceConstant::BaseInstance; + case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::DrawID: + return Shader::ReplaceConstant::DrawID; + default: + UNREACHABLE(); + } + }(it->second); + cbuf_replacements.emplace(key, converted_value); + return converted_value; +} + Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { const auto& regs{maxwell3d->regs}; const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; @@ -366,6 +402,7 @@ ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_com stage = Shader::Stage::Compute; local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc; texture_bound = kepler_compute->regs.tex_cb_index; + is_propietary_driver = texture_bound == 2; shared_memory_size = qmd.shared_alloc; workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}; } @@ -409,11 +446,14 @@ void FileEnvironment::Deserialize(std::ifstream& file) { u64 num_texture_types{}; u64 num_texture_pixel_formats{}; u64 num_cbuf_values{}; + u64 num_cbuf_replacement_values{}; file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) .read(reinterpret_cast<char*>(&num_texture_pixel_formats), sizeof(num_texture_pixel_formats)) .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) + .read(reinterpret_cast<char*>(&num_cbuf_replacement_values), + sizeof(num_cbuf_replacement_values)) .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) @@ -444,6 +484,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) { .read(reinterpret_cast<char*>(&value), sizeof(value)); cbuf_values.emplace(key, value); } + for (size_t i = 0; i < num_cbuf_replacement_values; ++i) { + u64 key; + Shader::ReplaceConstant value; + file.read(reinterpret_cast<char*>(&key), sizeof(key)) + .read(reinterpret_cast<char*>(&value), sizeof(value)); + cbuf_replacements.emplace(key, value); + } if (stage == Shader::Stage::Compute) { file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); @@ -455,6 +502,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) { file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); } } + is_propietary_driver = texture_bound == 2; } void FileEnvironment::Dump(u64 hash) { @@ -512,6 +560,16 @@ std::array<u32, 3> FileEnvironment::WorkgroupSize() const { return workgroup_size; } +std::optional<Shader::ReplaceConstant> FileEnvironment::GetReplaceConstBuffer(u32 bank, + u32 offset) { + const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset); + auto it = cbuf_replacements.find(key); + if (it == cbuf_replacements.end()) { + return std::nullopt; + } + return it->second; +} + void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, const std::filesystem::path& filename, u32 cache_version) try { std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app); diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 1342fab1e..d75987a52 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -60,6 +60,10 @@ public: void Serialize(std::ofstream& file) const; + bool HasHLEMacroState() const override { + return has_hle_engine_state; + } + protected: std::optional<u64> TryFindSize(); @@ -73,6 +77,7 @@ protected: std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u64, u32> cbuf_values; + std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements; u32 local_memory_size{}; u32 texture_bound{}; @@ -89,6 +94,7 @@ protected: u32 viewport_transform_state = 1; bool has_unbound_instructions = false; + bool has_hle_engine_state = false; }; class GraphicsEnvironment final : public GenericEnvironment { @@ -109,6 +115,8 @@ public: u32 ReadViewportTransformState() override; + std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override; + private: Tegra::Engines::Maxwell3D* maxwell3d{}; size_t stage_index{}; @@ -131,6 +139,11 @@ public: u32 ReadViewportTransformState() override; + std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer( + [[maybe_unused]] u32 bank, [[maybe_unused]] u32 offset) override { + return std::nullopt; + } + private: Tegra::Engines::KeplerCompute* kepler_compute{}; }; @@ -166,6 +179,13 @@ public: [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; + [[nodiscard]] std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, + u32 offset) override; + + [[nodiscard]] bool HasHLEMacroState() const override { + return cbuf_replacements.size() != 0; + } + void Dump(u64 hash) override; private: @@ -173,6 +193,7 @@ private: std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u64, u32> cbuf_values; + std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements; std::array<u32, 3> workgroup_size{}; u32 local_memory_size{}; u32 shared_memory_size{}; diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h index ee4240288..1bad83fb4 100644 --- a/src/video_core/texture_cache/descriptor_table.h +++ b/src/video_core/texture_cache/descriptor_table.h @@ -19,9 +19,7 @@ public: explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { - [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { - return false; - } + [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; } Refresh(gpu_addr, limit); return true; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 852ec2519..e9100091e 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); break; } + if (num_samples > 1) { + size.width *= NumSamplesX(config.msaa_mode); + size.height *= NumSamplesY(config.msaa_mode); + } if (type != ImageType::Linear) { // FIXME: Call this without passing *this layer_stride = CalculateLayerStride(*this); diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h index d552bccf0..203ac1b11 100644 --- a/src/video_core/texture_cache/samples_helper.h +++ b/src/video_core/texture_cache/samples_helper.h @@ -51,4 +51,48 @@ namespace VideoCommon { return 1; } +[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) { + using Tegra::Texture::MsaaMode; + switch (msaa_mode) { + case MsaaMode::Msaa1x1: + return 1; + case MsaaMode::Msaa2x1: + case MsaaMode::Msaa2x1_D3D: + case MsaaMode::Msaa2x2: + case MsaaMode::Msaa2x2_VC4: + case MsaaMode::Msaa2x2_VC12: + return 2; + case MsaaMode::Msaa4x2: + case MsaaMode::Msaa4x2_D3D: + case MsaaMode::Msaa4x2_VC8: + case MsaaMode::Msaa4x2_VC24: + case MsaaMode::Msaa4x4: + return 4; + } + ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode)); + return 1; +} + +[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) { + using Tegra::Texture::MsaaMode; + switch (msaa_mode) { + case MsaaMode::Msaa1x1: + case MsaaMode::Msaa2x1: + case MsaaMode::Msaa2x1_D3D: + return 1; + case MsaaMode::Msaa2x2: + case MsaaMode::Msaa2x2_VC4: + case MsaaMode::Msaa2x2_VC12: + case MsaaMode::Msaa4x2: + case MsaaMode::Msaa4x2_D3D: + case MsaaMode::Msaa4x2_VC8: + case MsaaMode::Msaa4x2_VC24: + return 2; + case MsaaMode::Msaa4x4: + return 4; + } + ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode)); + return 1; +} + } // namespace VideoCommon diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 1e2aad76a..9df6a2903 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -29,7 +29,7 @@ struct SlotId { }; template <class T> -requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> + requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> class SlotVector { public: class Iterator { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8e68a2e53..1b01990a4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -39,6 +39,12 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); sampler_descriptor.cubemap_anisotropy.Assign(1); + // These values were chosen based on typical peak swizzle data sizes seen in some titles + static constexpr size_t SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 8_MiB; + static constexpr size_t UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 1_MiB; + swizzle_data_buffer.resize_destructive(SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY); + unswizzle_data_buffer.resize_destructive(UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY); + // Make sure the first index is reserved for the null resources // This way the null resource becomes a compile time constant void(slot_images.insert(NullImageParams{})); @@ -90,7 +96,8 @@ void TextureCache<P>::RunGarbageCollector() { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, + swizzle_data_buffer); } if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, image_id); @@ -142,6 +149,13 @@ typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept { } template <class P> +typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept { + const auto image_view_id = VisitImageView(channel_state->graphics_image_table, + channel_state->graphics_image_view_ids, index); + return slot_image_views[image_view_id]; +} + +template <class P> void TextureCache<P>::MarkModification(ImageId id) noexcept { MarkModification(slot_images[id]); } @@ -461,7 +475,8 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, + swizzle_data_buffer); } } @@ -638,7 +653,28 @@ bool TextureCache<P>::ShouldWaitAsyncFlushes() const noexcept { template <class P> void TextureCache<P>::CommitAsyncFlushes() { // This is intentionally passing the value by copy - committed_downloads.push(uncommitted_downloads); + if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { + const std::span<const ImageId> download_ids = uncommitted_downloads; + if (download_ids.empty()) { + committed_downloads.emplace_back(std::move(uncommitted_downloads)); + uncommitted_downloads.clear(); + async_buffers.emplace_back(std::optional<AsyncBuffer>{}); + return; + } + size_t total_size_bytes = 0; + for (const ImageId image_id : download_ids) { + total_size_bytes += slot_images[image_id].unswizzled_size_bytes; + } + auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true); + for (const ImageId image_id : download_ids) { + Image& image = slot_images[image_id]; + const auto copies = FullDownloadCopies(image.info); + image.DownloadMemory(download_map, copies); + download_map.offset += Common::AlignUp(image.unswizzled_size_bytes, 64); + } + async_buffers.emplace_back(download_map); + } + committed_downloads.emplace_back(std::move(uncommitted_downloads)); uncommitted_downloads.clear(); } @@ -647,36 +683,58 @@ void TextureCache<P>::PopAsyncFlushes() { if (committed_downloads.empty()) { return; } - const std::span<const ImageId> download_ids = committed_downloads.front(); - if (download_ids.empty()) { - committed_downloads.pop(); - return; - } - size_t total_size_bytes = 0; - for (const ImageId image_id : download_ids) { - total_size_bytes += slot_images[image_id].unswizzled_size_bytes; - } - auto download_map = runtime.DownloadStagingBuffer(total_size_bytes); - const size_t original_offset = download_map.offset; - for (const ImageId image_id : download_ids) { - Image& image = slot_images[image_id]; - const auto copies = FullDownloadCopies(image.info); - image.DownloadMemory(download_map, copies); - download_map.offset += image.unswizzled_size_bytes; - } - // Wait for downloads to finish - runtime.Finish(); - - download_map.offset = original_offset; - std::span<u8> download_span = download_map.mapped_span; - for (const ImageId image_id : download_ids) { - const ImageBase& image = slot_images[image_id]; - const auto copies = FullDownloadCopies(image.info); - SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); - download_map.offset += image.unswizzled_size_bytes; - download_span = download_span.subspan(image.unswizzled_size_bytes); + if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { + const std::span<const ImageId> download_ids = committed_downloads.front(); + if (download_ids.empty()) { + committed_downloads.pop_front(); + async_buffers.pop_front(); + return; + } + auto download_map = *async_buffers.front(); + std::span<u8> download_span = download_map.mapped_span; + for (size_t i = download_ids.size(); i > 0; i--) { + const ImageBase& image = slot_images[download_ids[i - 1]]; + const auto copies = FullDownloadCopies(image.info); + download_map.offset -= Common::AlignUp(image.unswizzled_size_bytes, 64); + std::span<u8> download_span_alt = download_span.subspan(download_map.offset); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span_alt, + swizzle_data_buffer); + } + runtime.FreeDeferredStagingBuffer(download_map); + committed_downloads.pop_front(); + async_buffers.pop_front(); + } else { + const std::span<const ImageId> download_ids = committed_downloads.front(); + if (download_ids.empty()) { + committed_downloads.pop_front(); + return; + } + size_t total_size_bytes = 0; + for (const ImageId image_id : download_ids) { + total_size_bytes += slot_images[image_id].unswizzled_size_bytes; + } + auto download_map = runtime.DownloadStagingBuffer(total_size_bytes); + const size_t original_offset = download_map.offset; + for (const ImageId image_id : download_ids) { + Image& image = slot_images[image_id]; + const auto copies = FullDownloadCopies(image.info); + image.DownloadMemory(download_map, copies); + download_map.offset += image.unswizzled_size_bytes; + } + // Wait for downloads to finish + runtime.Finish(); + download_map.offset = original_offset; + std::span<u8> download_span = download_map.mapped_span; + for (const ImageId image_id : download_ids) { + const ImageBase& image = slot_images[image_id]; + const auto copies = FullDownloadCopies(image.info); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span, + swizzle_data_buffer); + download_map.offset += image.unswizzled_size_bytes; + download_span = download_span.subspan(image.unswizzled_size_bytes); + } + committed_downloads.pop_front(); } - committed_downloads.pop(); } template <class P> @@ -731,16 +789,25 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) const GPUVAddr gpu_addr = image.gpu_addr; if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { - gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); + gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(), + VideoCommon::CacheType::NoTextureCache); const auto uploads = FullUploadSwizzles(image.info); runtime.AccelerateImageUpload(image, staging, uploads); - } else if (True(image.flags & ImageFlagBits::Converted)) { - std::vector<u8> unswizzled_data(image.unswizzled_size_bytes); - auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); - ConvertImage(unswizzled_data, image.info, mapped_span, copies); + return; + } + const size_t guest_size_bytes = image.guest_size_bytes; + swizzle_data_buffer.resize_destructive(guest_size_bytes); + gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); + + if (True(image.flags & ImageFlagBits::Converted)) { + unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); + auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, + unswizzle_data_buffer); + ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies); image.UploadMemory(staging, copies); } else { - const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); + const auto copies = + UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span); image.UploadMemory(staging, copies); } } @@ -910,7 +977,7 @@ void TextureCache<P>::InvalidateScale(Image& image) { } template <class P> -u64 TextureCache<P>::GetScaledImageSizeBytes(ImageBase& image) { +u64 TextureCache<P>::GetScaledImageSizeBytes(const ImageBase& image) { const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale * Settings::values.resolution_info.up_scale); const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift + diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 587339a31..485eaabaa 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -17,6 +17,7 @@ #include "common/literals.h" #include "common/lru_cache.h" #include "common/polyfill_ranges.h" +#include "common/scratch_buffer.h" #include "video_core/compatible_formats.h" #include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" @@ -91,6 +92,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; /// True when the API can provide info about the memory of the device. static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; + /// True when the API can do asynchronous texture downloads. + static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS; static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()}; @@ -105,6 +108,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI using ImageView = typename P::ImageView; using Sampler = typename P::Sampler; using Framebuffer = typename P::Framebuffer; + using AsyncBuffer = typename P::AsyncBuffer; struct BlitImages { ImageId dst_id; @@ -125,6 +129,9 @@ public: /// Return a reference to the given image view id [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; + /// Get the imageview from the graphics descriptor table in the specified index + [[nodiscard]] ImageView& GetImageView(u32 index) noexcept; + /// Mark an image as modified from the GPU void MarkModification(ImageId id) noexcept; @@ -202,7 +209,7 @@ public: /// Create channel state. void CreateChannel(Tegra::Control::ChannelState& channel) final override; - std::mutex mutex; + std::recursive_mutex mutex; private: /// Iterate over all page indices in a range @@ -368,7 +375,7 @@ private: void InvalidateScale(Image& image); bool ScaleUp(Image& image); bool ScaleDown(Image& image); - u64 GetScaledImageSizeBytes(ImageBase& image); + u64 GetScaledImageSizeBytes(const ImageBase& image); Runtime& runtime; @@ -402,7 +409,8 @@ private: // TODO: This data structure is not optimal and it should be reworked std::vector<ImageId> uncommitted_downloads; - std::queue<std::vector<ImageId>> committed_downloads; + std::deque<std::vector<ImageId>> committed_downloads; + std::deque<std::optional<AsyncBuffer>> async_buffers; struct LRUItemParams { using ObjectType = ImageId; @@ -417,6 +425,9 @@ private: std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; + Common::ScratchBuffer<u8> swizzle_data_buffer; + Common::ScratchBuffer<u8> unswizzle_data_buffer; + u64 modification_tick = 0; u64 frame_tick = 0; }; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index e8c908b42..03acc68d9 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -505,7 +505,7 @@ void SwizzlePitchLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, const BufferImageCopy& copy, - std::span<const u8> input) { + std::span<const u8> input, Common::ScratchBuffer<u8>& tmp_buffer) { const Extent3D size = info.size; const LevelInfo level_info = MakeLevelInfo(info); const Extent2D tile_size = DefaultBlockSize(info.format); @@ -534,8 +534,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr tile_size.height, info.tile_width_spacing); const size_t subresource_size = sizes[level]; - const auto dst_data = std::make_unique<u8[]>(subresource_size); - const std::span<u8> dst(dst_data.get(), subresource_size); + tmp_buffer.resize_destructive(subresource_size); + const std::span<u8> dst(tmp_buffer); for (s32 layer = 0; layer < info.resources.layers; ++layer) { const std::span<const u8> src = input.subspan(host_offset); @@ -765,8 +765,9 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config } std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, - const ImageInfo& info, std::span<u8> output) { - const size_t guest_size_bytes = CalculateGuestSizeInBytes(info); + const ImageInfo& info, std::span<const u8> input, + std::span<u8> output) { + const size_t guest_size_bytes = input.size_bytes(); const u32 bpp_log2 = BytesPerBlockLog2(info.format); const Extent3D size = info.size; @@ -789,10 +790,6 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP .image_extent = size, }}; } - const auto input_data = std::make_unique<u8[]>(guest_size_bytes); - gpu_memory.ReadBlockUnsafe(gpu_addr, input_data.get(), guest_size_bytes); - const std::span<const u8> input(input_data.get(), guest_size_bytes); - const LevelInfo level_info = MakeLevelInfo(info); const s32 num_layers = info.resources.layers; const s32 num_levels = info.resources.levels; @@ -980,13 +977,14 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { } void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, - std::span<const BufferImageCopy> copies, std::span<const u8> memory) { + std::span<const BufferImageCopy> copies, std::span<const u8> memory, + Common::ScratchBuffer<u8>& tmp_buffer) { const bool is_pitch_linear = info.type == ImageType::Linear; for (const BufferImageCopy& copy : copies) { if (is_pitch_linear) { SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory); } else { - SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory); + SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory, tmp_buffer); } } } diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 5e28f4ab3..d103db8ae 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -7,6 +7,7 @@ #include <span> #include "common/common_types.h" +#include "common/scratch_buffer.h" #include "video_core/surface.h" #include "video_core/texture_cache/image_base.h" @@ -59,6 +60,7 @@ struct OverlapResult { [[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, + std::span<const u8> input, std::span<u8> output); [[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, @@ -76,7 +78,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8 [[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, - std::span<const BufferImageCopy> copies, std::span<const u8> memory); + std::span<const BufferImageCopy> copies, std::span<const u8> memory, + Common::ScratchBuffer<u8>& tmp_buffer); [[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info, const ImageInfo& overlap_info, u32 new_level, diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 85f1d13e0..5fa0d9620 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp @@ -57,7 +57,7 @@ NsightAftermathTracker::NsightAftermathTracker() { if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, - ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { + ShaderDebugInfoCallback, CrashDumpDescriptionCallback, nullptr, this))) { LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); return; } @@ -83,7 +83,7 @@ void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const { std::scoped_lock lock{mutex}; - GFSDK_Aftermath_ShaderHash hash; + GFSDK_Aftermath_ShaderBinaryHash hash; if (!GFSDK_Aftermath_SUCCEED( GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) { LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module"); @@ -121,8 +121,8 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 json_size = 0; if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, - GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr, - this, &json_size))) { + GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, this, + &json_size))) { LOG_ERROR(Render_Vulkan, "Failed to generate JSON"); return; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index c4d31681a..23d922e5d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -74,30 +74,6 @@ enum class NvidiaArchitecture { VoltaOrOlder, }; -constexpr std::array REQUIRED_EXTENSIONS{ - VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, - VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, -#ifdef _WIN32 - VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, -#endif -#ifdef __unix__ - VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, -#endif -}; - -constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{ - VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, - VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, - 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; @@ -286,24 +262,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica return format_properties; } -std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) { - const std::vector extensions = physical.EnumerateDeviceExtensionProperties(); - std::vector<std::string> supported_extensions; - supported_extensions.reserve(extensions.size()); - for (const auto& extension : extensions) { - supported_extensions.emplace_back(extension.extensionName); - } - return supported_extensions; -} - -bool IsExtensionSupported(std::span<const std::string> supported_extensions, - std::string_view extension) { - return std::ranges::find(supported_extensions, extension) != supported_extensions.end(); -} - NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, - std::span<const std::string> exts) { - if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { + const std::set<std::string, std::less<>>& exts) { + if (exts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; shading_rate_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; @@ -316,362 +277,55 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, return NvidiaArchitecture::AmpereOrNewer; } } - if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { + if (exts.contains(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { return NvidiaArchitecture::Turing; } return NvidiaArchitecture::VoltaOrOlder; } + +std::vector<const char*> ExtensionListForVulkan( + const std::set<std::string, std::less<>>& extensions) { + std::vector<const char*> output; + for (const auto& extension : extensions) { + output.push_back(extension.c_str()); + } + return output; +} + } // Anonymous namespace Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) - : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, - instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions( - physical)}, + : instance{instance_}, dld{dld_}, physical{physical_}, format_properties(GetFormatProperties(physical)) { - CheckSuitability(surface != nullptr); - SetupFamilies(surface); - SetupFeatures(); - SetupProperties(); - - const auto queue_cis = GetDeviceQueueCreateInfos(); - const std::vector extensions = LoadExtensions(surface != nullptr); - - VkPhysicalDeviceFeatures2 features2{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .pNext = nullptr, - .features{ - .robustBufferAccess = true, - .fullDrawIndexUint32 = false, - .imageCubeArray = true, - .independentBlend = true, - .geometryShader = true, - .tessellationShader = true, - .sampleRateShading = true, - .dualSrcBlend = true, - .logicOp = true, - .multiDrawIndirect = false, - .drawIndirectFirstInstance = false, - .depthClamp = true, - .depthBiasClamp = true, - .fillModeNonSolid = true, - .depthBounds = is_depth_bounds_supported, - .wideLines = true, - .largePoints = true, - .alphaToOne = false, - .multiViewport = true, - .samplerAnisotropy = true, - .textureCompressionETC2 = false, - .textureCompressionASTC_LDR = is_optimal_astc_supported, - .textureCompressionBC = false, - .occlusionQueryPrecise = true, - .pipelineStatisticsQuery = false, - .vertexPipelineStoresAndAtomics = true, - .fragmentStoresAndAtomics = true, - .shaderTessellationAndGeometryPointSize = false, - .shaderImageGatherExtended = true, - .shaderStorageImageExtendedFormats = false, - .shaderStorageImageMultisample = is_shader_storage_image_multisample, - .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported, - .shaderStorageImageWriteWithoutFormat = true, - .shaderUniformBufferArrayDynamicIndexing = false, - .shaderSampledImageArrayDynamicIndexing = false, - .shaderStorageBufferArrayDynamicIndexing = false, - .shaderStorageImageArrayDynamicIndexing = false, - .shaderClipDistance = true, - .shaderCullDistance = true, - .shaderFloat64 = is_shader_float64_supported, - .shaderInt64 = is_shader_int64_supported, - .shaderInt16 = is_shader_int16_supported, - .shaderResourceResidency = false, - .shaderResourceMinLod = false, - .sparseBinding = false, - .sparseResidencyBuffer = false, - .sparseResidencyImage2D = false, - .sparseResidencyImage3D = false, - .sparseResidency2Samples = false, - .sparseResidency4Samples = false, - .sparseResidency8Samples = false, - .sparseResidency16Samples = false, - .sparseResidencyAliased = false, - .variableMultisampleRate = false, - .inheritedQueries = false, - }, - }; - const void* first_next = &features2; - void** next = &features2.pNext; - - VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, - .pNext = nullptr, - .timelineSemaphore = true, - }; - SetNext(next, timeline_semaphore); - - VkPhysicalDevice16BitStorageFeatures bit16_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, - .pNext = nullptr, - .storageBuffer16BitAccess = true, - .uniformAndStorageBuffer16BitAccess = true, - .storagePushConstant16 = false, - .storageInputOutput16 = false, - }; - SetNext(next, bit16_storage); - - VkPhysicalDevice8BitStorageFeatures bit8_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, - .pNext = nullptr, - .storageBuffer8BitAccess = true, - .uniformAndStorageBuffer8BitAccess = true, - .storagePushConstant8 = false, - }; - SetNext(next, bit8_storage); - - VkPhysicalDeviceRobustness2FeaturesEXT robustness2{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, - .pNext = nullptr, - .robustBufferAccess2 = true, - .robustImageAccess2 = true, - .nullDescriptor = true, - }; - SetNext(next, robustness2); - - VkPhysicalDeviceHostQueryResetFeatures host_query_reset{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, - .pNext = nullptr, - .hostQueryReset = true, - }; - SetNext(next, host_query_reset); - - VkPhysicalDeviceVariablePointerFeatures variable_pointers{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, - .pNext = nullptr, - .variablePointersStorageBuffer = VK_TRUE, - .variablePointers = VK_TRUE, - }; - SetNext(next, variable_pointers); - - VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, - .pNext = nullptr, - .shaderDemoteToHelperInvocation = true, - }; - SetNext(next, demote); - - VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES, - .pNext = nullptr, - .shaderDrawParameters = true, - }; - SetNext(next, draw_parameters); - - VkPhysicalDeviceShaderFloat16Int8Features float16_int8; - if (is_int8_supported || is_float16_supported) { - float16_int8 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, - .pNext = nullptr, - .shaderFloat16 = is_float16_supported, - .shaderInt8 = is_int8_supported, - }; - SetNext(next, float16_int8); - } - if (!is_float16_supported) { - LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively"); - } - if (!is_int8_supported) { - LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively"); - } - - if (!nv_viewport_swizzle) { - LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles"); - } - - if (!nv_viewport_array2) { - LOG_INFO(Render_Vulkan, "Device doesn't support viewport masks"); - } - - if (!nv_geometry_shader_passthrough) { - LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders"); - } - - VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout; - if (khr_uniform_buffer_standard_layout) { - std430_layout = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, - .pNext = nullptr, - .uniformBufferStandardLayout = true, - }; - SetNext(next, std430_layout); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs"); - } - - VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8; - if (ext_index_type_uint8) { - index_type_uint8 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, - .pNext = nullptr, - .indexTypeUint8 = true, - }; - SetNext(next, index_type_uint8); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes"); - } - - VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart; - if (is_topology_list_restart_supported || is_patch_list_restart_supported) { - primitive_topology_list_restart = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, - .pNext = nullptr, - .primitiveTopologyListRestart = is_topology_list_restart_supported, - .primitiveTopologyPatchListRestart = is_patch_list_restart_supported, - }; - SetNext(next, primitive_topology_list_restart); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart"); - } - - VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback; - if (ext_transform_feedback) { - transform_feedback = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT, - .pNext = nullptr, - .transformFeedback = true, - .geometryStreams = true, - }; - SetNext(next, transform_feedback); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks"); - } - - VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border; - if (ext_custom_border_color) { - custom_border = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, - .pNext = nullptr, - .customBorderColors = VK_TRUE, - .customBorderColorWithoutFormat = VK_TRUE, - }; - SetNext(next, custom_border); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); - } - - VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; - if (ext_extended_dynamic_state) { - dynamic_state = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, - .pNext = nullptr, - .extendedDynamicState = VK_TRUE, - }; - SetNext(next, dynamic_state); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); - } - - VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; - if (ext_line_rasterization) { - line_raster = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, - .pNext = nullptr, - .rectangularLines = VK_TRUE, - .bresenhamLines = VK_FALSE, - .smoothLines = VK_TRUE, - .stippledRectangularLines = VK_FALSE, - .stippledBresenhamLines = VK_FALSE, - .stippledSmoothLines = VK_FALSE, - }; - SetNext(next, line_raster); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support smooth lines"); - } - - if (!ext_conservative_rasterization) { - LOG_INFO(Render_Vulkan, "Device doesn't support conservative rasterization"); - } - - VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex; - if (ext_provoking_vertex) { - provoking_vertex = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, - .pNext = nullptr, - .provokingVertexLast = VK_TRUE, - .transformFeedbackPreservesProvokingVertex = VK_TRUE, - }; - SetNext(next, provoking_vertex); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support provoking vertex last"); - } + // Get suitability and device properties. + const bool is_suitable = GetSuitability(surface != nullptr); - VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input_dynamic; - if (ext_vertex_input_dynamic_state) { - vertex_input_dynamic = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT, - .pNext = nullptr, - .vertexInputDynamicState = VK_TRUE, - }; - SetNext(next, vertex_input_dynamic); - } else { - LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state"); - } - - VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; - if (ext_shader_atomic_int64) { - atomic_int64 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, - .pNext = nullptr, - .shaderBufferInt64Atomics = VK_TRUE, - .shaderSharedInt64Atomics = VK_TRUE, - }; - SetNext(next, atomic_int64); - } + const VkDriverId driver_id = properties.driver.driverID; + const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; + const bool is_amd_driver = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; + const bool is_amd = is_amd_driver || is_radv; + const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; + const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; + const bool is_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY; + const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK; - VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout; - if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) { - workgroup_layout = { - .sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR, - .pNext = nullptr, - .workgroupMemoryExplicitLayout = VK_TRUE, - .workgroupMemoryExplicitLayoutScalarBlockLayout = VK_TRUE, - .workgroupMemoryExplicitLayout8BitAccess = VK_TRUE, - .workgroupMemoryExplicitLayout16BitAccess = VK_TRUE, - }; - SetNext(next, workgroup_layout); - } else if (khr_workgroup_memory_explicit_layout) { - // TODO(lat9nq): Find a proper fix for this - LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a " - "yuzu bug when host driver does not support 16-bit integers"); - khr_workgroup_memory_explicit_layout = false; + if (is_mvk && !is_suitable) { + LOG_WARNING(Render_Vulkan, "Unsuitable driver is MoltenVK, continuing anyway"); + } else if (!is_suitable) { + throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER); } - VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; - if (khr_pipeline_executable_properties) { - LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times"); - executable_properties = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR, - .pNext = nullptr, - .pipelineExecutableInfo = VK_TRUE, - }; - SetNext(next, executable_properties); - } - - if (!ext_depth_range_unrestricted) { - LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); - } + SetupFamilies(surface); + const auto queue_cis = GetDeviceQueueCreateInfos(); - VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; - if (ext_depth_clip_control) { - depth_clip_control_features = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT, - .pNext = nullptr, - .depthClipControl = VK_TRUE, - }; - SetNext(next, depth_clip_control_features); - } + // GetSuitability has already configured the linked list of features for us. + // Reuse it here. + const void* first_next = &features2; - VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; - if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { + VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv{}; + if (Settings::values.enable_nsight_aftermath && extensions.device_diagnostics_config) { nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); diagnostics_nv = { @@ -683,93 +337,116 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; first_next = &diagnostics_nv; } - logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); - is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; - is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; - is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || - properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; + is_blit_depth_stencil_supported = TestDepthStencilBlits(); + is_optimal_astc_supported = ComputeIsOptimalAstcSupported(); + is_warp_potentially_bigger = !extensions.subgroup_size_control || + properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize; + + is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; + is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || + properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; + + supports_d24_depth = + IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); CollectPhysicalMemoryInfo(); - CollectTelemetryParameters(); CollectToolingInfo(); - if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { + if (is_nvidia) { + const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; const auto arch = GetNvidiaArchitecture(physical, supported_extensions); switch (arch) { case NvidiaArchitecture::AmpereOrNewer: - LOG_WARNING(Render_Vulkan, "Blacklisting Ampere devices from float16 math"); - is_float16_supported = false; + LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); + features.shader_float16_int8.shaderFloat16 = false; break; case NvidiaArchitecture::Turing: break; case NvidiaArchitecture::VoltaOrOlder: - LOG_WARNING(Render_Vulkan, "Blacklisting Volta and older from VK_KHR_push_descriptor"); - khr_push_descriptor = false; + if (nv_major_version < 527) { + LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); + extensions.push_descriptor = false; + loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + } break; } - const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff; if (nv_major_version >= 510) { LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); cant_blit_msaa = true; } } - const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; - if (ext_extended_dynamic_state && is_radv) { + if (extensions.extended_dynamic_state && is_radv) { // Mask driver version variant - const u32 version = (properties.driverVersion << 3) >> 3; + const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { LOG_WARNING(Render_Vulkan, "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); - ext_extended_dynamic_state = false; + extensions.extended_dynamic_state = false; + loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + } + } + if (extensions.extended_dynamic_state2 && is_radv) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { + LOG_WARNING( + Render_Vulkan, + "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); + features.extended_dynamic_state2.extendedDynamicState2 = false; + features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; + features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; + extensions.extended_dynamic_state2 = false; + loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } } - if (ext_vertex_input_dynamic_state && is_radv) { + if (extensions.vertex_input_dynamic_state && is_radv) { // TODO(ameerj): Blacklist only offending driver versions // TODO(ameerj): Confirm if RDNA1 is affected const bool is_rdna2 = - IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); + supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); if (is_rdna2) { LOG_WARNING(Render_Vulkan, "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); - ext_vertex_input_dynamic_state = false; + extensions.vertex_input_dynamic_state = false; + loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); } } - sets_per_pool = 64; - const bool is_amd = - driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; - if (is_amd) { + sets_per_pool = 64; + if (is_amd_driver) { // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. sets_per_pool = 96; // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. - if (!is_float16_supported) { - LOG_WARNING( - Render_Vulkan, - "AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT"); + if (!features.shader_float16_int8.shaderFloat16) { + LOG_WARNING(Render_Vulkan, + "AMD GCN4 and earlier have broken VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT"); has_broken_cube_compatibility = true; } } - const bool is_amd_or_radv = is_amd || is_radv; - if (ext_sampler_filter_minmax && is_amd_or_radv) { + if (extensions.sampler_filter_minmax && is_amd) { // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. - if (!is_float16_supported) { + if (!features.shader_float16_int8.shaderFloat16) { LOG_WARNING(Render_Vulkan, - "Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax"); - ext_sampler_filter_minmax = false; + "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); + extensions.sampler_filter_minmax = false; + loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); } } - const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; - const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; - if (ext_vertex_input_dynamic_state && is_intel_windows) { - LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); - ext_vertex_input_dynamic_state = false; + if (extensions.vertex_input_dynamic_state && is_intel_windows) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { + LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); + extensions.vertex_input_dynamic_state = false; + loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + } } - if (is_float16_supported && is_intel_windows) { + if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. - LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math"); - is_float16_supported = false; + LOG_WARNING(Render_Vulkan, "Intel has broken float16 math"); + features.shader_float16_int8.shaderFloat16 = false; } if (is_intel_windows) { LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); @@ -779,10 +456,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format"); must_emulate_bgr565 = true; } + if (is_mvk) { + LOG_WARNING(Render_Vulkan, + "MVK driver breaks when using more than 16 vertex attributes/bindings"); + properties.properties.limits.maxVertexInputAttributes = + std::min(properties.properties.limits.maxVertexInputAttributes, 16U); + properties.properties.limits.maxVertexInputBindings = + std::min(properties.properties.limits.maxVertexInputBindings, 16U); + } - supports_d24_depth = - IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); + logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), + first_next, dld); graphics_queue = logical.GetQueue(graphics_family); present_queue = logical.GetQueue(present_family); @@ -837,7 +521,7 @@ void Device::SaveShader(std::span<const u32> spirv) const { } } -bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { +bool Device::ComputeIsOptimalAstcSupported() const { // Disable for now to avoid converting ASTC twice. static constexpr std::array astc_formats = { VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, @@ -855,7 +539,7 @@ bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) co VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_SRGB_BLOCK, VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK, }; - if (!features.textureCompressionASTC_LDR) { + if (!features.features.textureCompressionASTC_LDR) { return false; } const auto format_feature_usage{ @@ -893,7 +577,7 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want } std::string Device::GetDriverName() const { - switch (driver_id) { + switch (properties.driver.driverID) { case VK_DRIVER_ID_AMD_PROPRIETARY: return "AMD"; case VK_DRIVER_ID_AMD_OPEN_SOURCE: @@ -909,460 +593,336 @@ std::string Device::GetDriverName() const { case VK_DRIVER_ID_MESA_LLVMPIPE: return "LAVAPIPE"; default: - return vendor_name; + return properties.driver.driverName; } } -static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { - std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; +bool Device::ShouldBoostClocks() const { + const auto driver_id = properties.driver.driverID; + const auto vendor_id = properties.properties.vendorID; + const auto device_id = properties.properties.deviceID; - if (available_version < VK_API_VERSION_1_2) { - extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(), - REQUIRED_EXTENSIONS_BEFORE_1_2.end()); - } + const bool validated_driver = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || + driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || + driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS || + driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; - if (available_version < VK_API_VERSION_1_3) { - extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(), - REQUIRED_EXTENSIONS_BEFORE_1_3.end()); - } + const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; - return extensions; + return validated_driver && !is_steam_deck; } -void Device::CheckSuitability(bool requires_swapchain) const { - std::vector<const char*> required_extensions = - ExtensionsRequiredForInstanceVersion(instance_version); - std::vector<const char*> available_extensions; +bool Device::GetSuitability(bool requires_swapchain) { + // Assume we will be suitable. + bool suitable = true; - if (requires_swapchain) { - required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - } + // Configure properties. + properties.properties = physical.GetProperties(); + + // Set instance version. + instance_version = properties.properties.apiVersion; + // Minimum of API version 1.1 is required. (This is well-supported.) + ASSERT(instance_version >= VK_API_VERSION_1_1); + + // Get available extensions. auto extension_properties = physical.EnumerateDeviceExtensionProperties(); + // Get the set of supported extensions. + supported_extensions.clear(); for (const VkExtensionProperties& property : extension_properties) { - available_extensions.push_back(property.extensionName); + supported_extensions.insert(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; - }); + // Generate list of extensions to load. + loaded_extensions.clear(); - if (!found) { - LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name); - has_all_required_extensions = false; - } +#define EXTENSION(prefix, macro_name, var_name) \ + if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \ + loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \ + extensions.var_name = true; \ } - - if (!has_all_required_extensions) { - throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); +#define FEATURE_EXTENSION(prefix, struct_name, macro_name, var_name) \ + if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \ + loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \ + extensions.var_name = true; \ } - struct LimitTuple { - u32 minimum; - u32 value; - const char* name; - }; - const VkPhysicalDeviceLimits& limits{properties.limits}; - const std::array limits_report{ - LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"}, - LimitTuple{16, limits.maxViewports, "maxViewports"}, - LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"}, - LimitTuple{8, limits.maxClipDistances, "maxClipDistances"}, - }; - for (const auto& tuple : limits_report) { - if (tuple.value < tuple.minimum) { - LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name, - tuple.minimum, tuple.value); - throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); - } + if (instance_version < VK_API_VERSION_1_2) { + FOR_EACH_VK_FEATURE_1_2(FEATURE_EXTENSION); + } + if (instance_version < VK_API_VERSION_1_3) { + FOR_EACH_VK_FEATURE_1_3(FEATURE_EXTENSION); } - VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{}; - demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES; - demote.pNext = nullptr; - VkPhysicalDeviceVariablePointerFeatures variable_pointers{}; - variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES; - variable_pointers.pNext = &demote; + FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); + FOR_EACH_VK_EXTENSION(EXTENSION); +#ifdef _WIN32 + FOR_EACH_VK_EXTENSION_WIN32(EXTENSION); +#endif - VkPhysicalDeviceRobustness2FeaturesEXT robustness2{}; - robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; - robustness2.pNext = &variable_pointers; +#undef FEATURE_EXTENSION +#undef EXTENSION - VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{}; - timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; - timeline_semaphore.pNext = &robustness2; + // Some extensions are mandatory. Check those. +#define CHECK_EXTENSION(extension_name) \ + if (!loaded_extensions.contains(extension_name)) { \ + LOG_ERROR(Render_Vulkan, "Missing required extension {}", extension_name); \ + suitable = false; \ + } - VkPhysicalDevice16BitStorageFeatures bit16_storage{}; - bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; - bit16_storage.pNext = &timeline_semaphore; +#define LOG_EXTENSION(extension_name) \ + if (!loaded_extensions.contains(extension_name)) { \ + LOG_INFO(Render_Vulkan, "Device doesn't support extension {}", extension_name); \ + } - VkPhysicalDevice8BitStorageFeatures bit8_storage{}; - bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES; - bit8_storage.pNext = &bit16_storage; + FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION); + FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION); +#ifdef _WIN32 + FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION); +#else + FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION); +#endif - VkPhysicalDeviceHostQueryResetFeatures host_query_reset{}; - host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES; - host_query_reset.pNext = &bit8_storage; + if (requires_swapchain) { + CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } - VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{}; - draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; - draw_parameters.pNext = &host_query_reset; +#undef LOG_EXTENSION +#undef CHECK_EXTENSION - VkPhysicalDeviceFeatures2 features2{}; + // Generate the linked list of features to test. features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - features2.pNext = &draw_parameters; - physical.GetFeatures2(features2); + // Set next pointer. + void** next = &features2.pNext; - const VkPhysicalDeviceFeatures& features{features2.features}; - std::array feature_report{ - std::make_pair(features.robustBufferAccess, "robustBufferAccess"), - std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), - std::make_pair(features.imageCubeArray, "imageCubeArray"), - std::make_pair(features.independentBlend, "independentBlend"), - std::make_pair(features.depthClamp, "depthClamp"), - std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"), - std::make_pair(features.largePoints, "largePoints"), - std::make_pair(features.multiViewport, "multiViewport"), - std::make_pair(features.depthBiasClamp, "depthBiasClamp"), - std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"), - std::make_pair(features.wideLines, "wideLines"), - std::make_pair(features.geometryShader, "geometryShader"), - std::make_pair(features.tessellationShader, "tessellationShader"), - std::make_pair(features.sampleRateShading, "sampleRateShading"), - std::make_pair(features.dualSrcBlend, "dualSrcBlend"), - std::make_pair(features.logicOp, "logicOp"), - std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), - std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), - std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), - std::make_pair(features.shaderStorageImageWriteWithoutFormat, - "shaderStorageImageWriteWithoutFormat"), - std::make_pair(features.shaderClipDistance, "shaderClipDistance"), - std::make_pair(features.shaderCullDistance, "shaderCullDistance"), - 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"), - std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"), - std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), - std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, - "uniformAndStorageBuffer16BitAccess"), - std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"), - std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, - "uniformAndStorageBuffer8BitAccess"), - std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), - std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"), - }; + // Test all features we know about. If the feature is not available in core at our + // current API version, and was not enabled by an extension, skip testing the feature. + // We set the structure sType explicitly here as it is zeroed by the constructor. +#define FEATURE(prefix, struct_name, macro_name, var_name) \ + features.var_name.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES; \ + SetNext(next, features.var_name); - bool has_all_required_features = true; - for (const auto& [is_supported, name] : feature_report) { - if (!is_supported) { - LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); - has_all_required_features = false; - } +#define EXT_FEATURE(prefix, struct_name, macro_name, var_name) \ + if (extensions.var_name) { \ + features.var_name.sType = \ + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES_##prefix; \ + SetNext(next, features.var_name); \ } - if (!has_all_required_features) { - throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); + FOR_EACH_VK_FEATURE_1_1(FEATURE); + FOR_EACH_VK_FEATURE_EXT(EXT_FEATURE); + if (instance_version >= VK_API_VERSION_1_2) { + FOR_EACH_VK_FEATURE_1_2(FEATURE); + } else { + FOR_EACH_VK_FEATURE_1_2(EXT_FEATURE); } -} - -std::vector<const char*> Device::LoadExtensions(bool requires_surface) { - std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version); - if (requires_surface) { - extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + if (instance_version >= VK_API_VERSION_1_3) { + FOR_EACH_VK_FEATURE_1_3(FEATURE); + } else { + FOR_EACH_VK_FEATURE_1_3(EXT_FEATURE); } - bool has_khr_shader_float16_int8{}; - bool has_khr_workgroup_memory_explicit_layout{}; - bool has_khr_pipeline_executable_properties{}; - bool has_khr_image_format_list{}; - bool has_khr_swapchain_mutable_format{}; - bool has_ext_subgroup_size_control{}; - bool has_ext_transform_feedback{}; - bool has_ext_custom_border_color{}; - bool has_ext_extended_dynamic_state{}; - bool has_ext_shader_atomic_int64{}; - bool has_ext_provoking_vertex{}; - bool has_ext_vertex_input_dynamic_state{}; - bool has_ext_line_rasterization{}; - bool has_ext_primitive_topology_list_restart{}; - bool has_ext_depth_clip_control{}; - for (const std::string& extension : supported_extensions) { - const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name, - bool push) { - if (extension != name) { - return; - } - if (push) { - extensions.push_back(name); - } - if (status) { - status->get() = true; - } - }; - test(nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); - test(nv_viewport_array2, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, true); - test(nv_geometry_shader_passthrough, VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME, - true); - test(khr_uniform_buffer_standard_layout, - VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); - test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true); - test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true); - test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); - test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); - test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); - test(has_ext_primitive_topology_list_restart, - VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true); - test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true); - test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, - true); - test(ext_tooling_info, VK_EXT_TOOLING_INFO_EXTENSION_NAME, true); - test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true); - test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, - true); - test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false); - test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); - test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); - test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); - test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true); - test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false); - test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, - false); - test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false); - test(has_khr_workgroup_memory_explicit_layout, - VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false); - test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); - test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, - false); - test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); - test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true); - if (Settings::values.enable_nsight_aftermath) { - test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, - true); - } - if (Settings::values.renderer_shader_feedback) { - test(has_khr_pipeline_executable_properties, - VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false); - } - } - VkPhysicalDeviceFeatures2 features{}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - - VkPhysicalDeviceProperties2 physical_properties{}; - physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - - if (has_khr_shader_float16_int8) { - VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features; - float16_int8_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; - float16_int8_features.pNext = nullptr; - features.pNext = &float16_int8_features; - - physical.GetFeatures2(features); - is_float16_supported = float16_int8_features.shaderFloat16; - is_int8_supported = float16_int8_features.shaderInt8; - extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); - } - if (has_ext_subgroup_size_control) { - VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features; - subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES; - subgroup_features.pNext = nullptr; - features.pNext = &subgroup_features; - physical.GetFeatures2(features); - - VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties; - subgroup_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; - subgroup_properties.pNext = nullptr; - physical_properties.pNext = &subgroup_properties; - physical.GetProperties2(physical_properties); +#undef EXT_FEATURE +#undef FEATURE - is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize; + // Perform the feature test. + physical.GetFeatures2(features2); + features.features = features2.features; - if (subgroup_features.subgroupSizeControl && - subgroup_properties.minSubgroupSize <= GuestWarpSize && - subgroup_properties.maxSubgroupSize >= GuestWarpSize) { - extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); - guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages; - ext_subgroup_size_control = true; - } - } else { - is_warp_potentially_bigger = true; - } - if (has_ext_provoking_vertex) { - VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex; - provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; - provoking_vertex.pNext = nullptr; - features.pNext = &provoking_vertex; - physical.GetFeatures2(features); - - if (provoking_vertex.provokingVertexLast && - provoking_vertex.transformFeedbackPreservesProvokingVertex) { - extensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); - ext_provoking_vertex = true; - } - } - if (has_ext_vertex_input_dynamic_state) { - VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input; - vertex_input.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT; - vertex_input.pNext = nullptr; - features.pNext = &vertex_input; - physical.GetFeatures2(features); - - if (vertex_input.vertexInputDynamicState) { - extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - ext_vertex_input_dynamic_state = true; - } - } - if (has_ext_shader_atomic_int64) { - VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; - atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES; - atomic_int64.pNext = nullptr; - features.pNext = &atomic_int64; - physical.GetFeatures2(features); - - if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) { - extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); - ext_shader_atomic_int64 = true; - } + // Some features are mandatory. Check those. +#define CHECK_FEATURE(feature, name) \ + if (!features.feature.name) { \ + LOG_ERROR(Render_Vulkan, "Missing required feature {}", #name); \ + suitable = false; \ } - if (has_ext_transform_feedback) { - VkPhysicalDeviceTransformFeedbackFeaturesEXT tfb_features; - tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; - tfb_features.pNext = nullptr; - features.pNext = &tfb_features; - physical.GetFeatures2(features); - - VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties; - tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; - tfb_properties.pNext = nullptr; - physical_properties.pNext = &tfb_properties; - physical.GetProperties2(physical_properties); - if (tfb_features.transformFeedback && tfb_features.geometryStreams && - tfb_properties.maxTransformFeedbackStreams >= 4 && - tfb_properties.maxTransformFeedbackBuffers && tfb_properties.transformFeedbackQueries && - tfb_properties.transformFeedbackDraw) { - extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); - ext_transform_feedback = true; - } - } - if (has_ext_custom_border_color) { - VkPhysicalDeviceCustomBorderColorFeaturesEXT border_features; - border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; - border_features.pNext = nullptr; - features.pNext = &border_features; - physical.GetFeatures2(features); - - if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) { - extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); - ext_custom_border_color = true; - } +#define LOG_FEATURE(feature, name) \ + if (!features.feature.name) { \ + LOG_INFO(Render_Vulkan, "Device doesn't support feature {}", #name); \ } - if (has_ext_extended_dynamic_state) { - VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state; - extended_dynamic_state.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; - extended_dynamic_state.pNext = nullptr; - features.pNext = &extended_dynamic_state; - physical.GetFeatures2(features); - - if (extended_dynamic_state.extendedDynamicState) { - extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - ext_extended_dynamic_state = true; - } + + FOR_EACH_VK_RECOMMENDED_FEATURE(LOG_FEATURE); + FOR_EACH_VK_MANDATORY_FEATURE(CHECK_FEATURE); + +#undef LOG_FEATURE +#undef CHECK_FEATURE + + // Generate linked list of properties. + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + + // Set next pointer. + next = &properties2.pNext; + + // Get driver info. + properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + SetNext(next, properties.driver); + + // Retrieve relevant extension properties. + if (extensions.shader_float_controls) { + properties.float_controls.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES; + SetNext(next, properties.float_controls); } - if (has_ext_line_rasterization) { - VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; - line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; - line_raster.pNext = nullptr; - features.pNext = &line_raster; - physical.GetFeatures2(features); - if (line_raster.rectangularLines && line_raster.smoothLines) { - extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); - ext_line_rasterization = true; - } + if (extensions.push_descriptor) { + properties.push_descriptor.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; + SetNext(next, properties.push_descriptor); } - if (has_ext_depth_clip_control) { - VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; - depth_clip_control_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT; - depth_clip_control_features.pNext = nullptr; - features.pNext = &depth_clip_control_features; - physical.GetFeatures2(features); - - if (depth_clip_control_features.depthClipControl) { - extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); - ext_depth_clip_control = true; - } + if (extensions.subgroup_size_control) { + properties.subgroup_size_control.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; + SetNext(next, properties.subgroup_size_control); } - if (has_khr_workgroup_memory_explicit_layout) { - VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; - layout.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR; - layout.pNext = nullptr; - features.pNext = &layout; - physical.GetFeatures2(features); - - if (layout.workgroupMemoryExplicitLayout && - layout.workgroupMemoryExplicitLayout8BitAccess && - layout.workgroupMemoryExplicitLayout16BitAccess && - layout.workgroupMemoryExplicitLayoutScalarBlockLayout) { - extensions.push_back(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); - khr_workgroup_memory_explicit_layout = true; - } + if (extensions.transform_feedback) { + properties.transform_feedback.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; + SetNext(next, properties.transform_feedback); } - if (has_khr_pipeline_executable_properties) { - VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; - executable_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; - executable_properties.pNext = nullptr; - features.pNext = &executable_properties; - physical.GetFeatures2(features); - - if (executable_properties.pipelineExecutableInfo) { - extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); - khr_pipeline_executable_properties = true; + + // Perform the property fetch. + physical.GetProperties2(properties2); + properties.properties = properties2.properties; + + // Unload extensions if feature support is insufficient. + RemoveUnsuitableExtensions(); + + // Check limits. + struct Limit { + u32 minimum; + u32 value; + const char* name; + }; + + const VkPhysicalDeviceLimits& limits{properties.properties.limits}; + const std::array limits_report{ + Limit{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"}, + Limit{16, limits.maxViewports, "maxViewports"}, + Limit{8, limits.maxColorAttachments, "maxColorAttachments"}, + Limit{8, limits.maxClipDistances, "maxClipDistances"}, + }; + + for (const auto& [min, value, name] : limits_report) { + if (value < min) { + LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", name, min, value); + suitable = false; } } - if (has_ext_primitive_topology_list_restart) { - VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{}; - primitive_topology_list_restart.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT; - primitive_topology_list_restart.pNext = nullptr; - features.pNext = &primitive_topology_list_restart; - physical.GetFeatures2(features); - - is_topology_list_restart_supported = - primitive_topology_list_restart.primitiveTopologyListRestart; - is_patch_list_restart_supported = - primitive_topology_list_restart.primitiveTopologyPatchListRestart; - } - if (has_khr_image_format_list && has_khr_swapchain_mutable_format) { - extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); - extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); - khr_swapchain_mutable_format = true; - } - if (khr_push_descriptor) { - VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; - push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; - push_descriptor.pNext = nullptr; - physical_properties.pNext = &push_descriptor; - physical.GetProperties2(physical_properties); + // Return whether we were suitable. + return suitable; +} - max_push_descriptors = push_descriptor.maxPushDescriptors; +void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) { + if (loaded_extensions.contains(extension_name) && !is_suitable) { + LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name); + loaded_extensions.erase(extension_name); } - return extensions; +} + +void Device::RemoveUnsuitableExtensions() { + // VK_EXT_custom_border_color + extensions.custom_border_color = features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat; + RemoveExtensionIfUnsuitable(extensions.custom_border_color, + VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + + // VK_EXT_depth_clip_control + extensions.depth_clip_control = features.depth_clip_control.depthClipControl; + RemoveExtensionIfUnsuitable(extensions.depth_clip_control, + VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); + + // VK_EXT_extended_dynamic_state + extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; + RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, + VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + + // VK_EXT_extended_dynamic_state2 + extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; + RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, + VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); + + // VK_EXT_extended_dynamic_state3 + dynamic_state3_blending = + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation && + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; + dynamic_state3_enables = + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable && + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable; + + extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; + dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; + dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; + RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, + VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + + // VK_EXT_provoking_vertex + extensions.provoking_vertex = + features.provoking_vertex.provokingVertexLast && + features.provoking_vertex.transformFeedbackPreservesProvokingVertex; + RemoveExtensionIfUnsuitable(extensions.provoking_vertex, + VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + + // VK_KHR_shader_atomic_int64 + extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && + features.shader_atomic_int64.shaderSharedInt64Atomics; + RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, + VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); + + // VK_EXT_shader_demote_to_helper_invocation + extensions.shader_demote_to_helper_invocation = + features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; + RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, + VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); + + // VK_EXT_subgroup_size_control + extensions.subgroup_size_control = + features.subgroup_size_control.subgroupSizeControl && + properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && + properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; + RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, + VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); + + // VK_EXT_transform_feedback + extensions.transform_feedback = + features.transform_feedback.transformFeedback && + features.transform_feedback.geometryStreams && + properties.transform_feedback.maxTransformFeedbackStreams >= 4 && + properties.transform_feedback.maxTransformFeedbackBuffers > 0 && + properties.transform_feedback.transformFeedbackQueries && + properties.transform_feedback.transformFeedbackDraw; + RemoveExtensionIfUnsuitable(extensions.transform_feedback, + VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); + + // VK_EXT_vertex_input_dynamic_state + extensions.vertex_input_dynamic_state = + features.vertex_input_dynamic_state.vertexInputDynamicState; + RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, + VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + + // VK_KHR_pipeline_executable_properties + if (Settings::values.renderer_shader_feedback.GetValue()) { + extensions.pipeline_executable_properties = + features.pipeline_executable_properties.pipelineExecutableInfo; + RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, + VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); + } else { + extensions.pipeline_executable_properties = false; + loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); + } + + // VK_KHR_workgroup_memory_explicit_layout + extensions.workgroup_memory_explicit_layout = + features.features.shaderInt16 && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; + RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, + VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); } void Device::SetupFamilies(VkSurfaceKHR surface) { @@ -1392,55 +952,12 @@ void Device::SetupFamilies(VkSurfaceKHR surface) { LOG_ERROR(Render_Vulkan, "Device lacks a present queue"); throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } - graphics_family = *graphics; - present_family = *present; -} - -void Device::SetupFeatures() { - const VkPhysicalDeviceFeatures features{physical.GetFeatures()}; - is_depth_bounds_supported = features.depthBounds; - is_formatless_image_load_supported = features.shaderStorageImageReadWithoutFormat; - is_shader_float64_supported = features.shaderFloat64; - is_shader_int64_supported = features.shaderInt64; - is_shader_int16_supported = features.shaderInt16; - is_shader_storage_image_multisample = features.shaderStorageImageMultisample; - is_blit_depth_stencil_supported = TestDepthStencilBlits(); - is_optimal_astc_supported = IsOptimalAstcSupported(features); - - const VkPhysicalDeviceLimits& limits{properties.limits}; - max_vertex_input_attributes = limits.maxVertexInputAttributes; - max_vertex_input_bindings = limits.maxVertexInputBindings; -} - -void Device::SetupProperties() { - float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES; - - VkPhysicalDeviceProperties2KHR properties2{}; - properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - properties2.pNext = &float_controls; - - physical.GetProperties2(properties2); -} - -void Device::CollectTelemetryParameters() { - VkPhysicalDeviceDriverProperties driver{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, - .pNext = nullptr, - .driverID = {}, - .driverName = {}, - .driverInfo = {}, - .conformanceVersion = {}, - }; - - VkPhysicalDeviceProperties2 device_properties{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &driver, - .properties = {}, - }; - physical.GetProperties2(device_properties); - - driver_id = driver.driverID; - vendor_name = driver.driverName; + if (graphics) { + graphics_family = *graphics; + } + if (present) { + present_family = *present; + } } u64 Device::GetDeviceMemoryUsage() const { @@ -1458,7 +975,8 @@ u64 Device::GetDeviceMemoryUsage() const { void Device::CollectPhysicalMemoryInfo() { VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; - const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr); + const auto mem_info = + physical.GetMemoryProperties(extensions.memory_budget ? &budget : nullptr); const auto& mem_properties = mem_info.memoryProperties; const size_t num_properties = mem_properties.memoryHeapCount; device_access_memory = 0; @@ -1474,7 +992,7 @@ void Device::CollectPhysicalMemoryInfo() { if (is_heap_local) { local_memory += mem_properties.memoryHeaps[element].size; } - if (ext_memory_budget) { + if (extensions.memory_budget) { device_initial_usage += budget.heapUsage[element]; device_access_memory += budget.heapBudget[element]; continue; @@ -1490,7 +1008,7 @@ void Device::CollectPhysicalMemoryInfo() { } void Device::CollectToolingInfo() { - if (!ext_tooling_info) { + if (!extensions.tooling_info) { return; } auto tools{physical.GetPhysicalDeviceToolProperties()}; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 6a26c4e6e..0662a2d9f 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -3,6 +3,7 @@ #pragma once +#include <set> #include <span> #include <string> #include <unordered_map> @@ -11,6 +12,156 @@ #include "common/common_types.h" #include "video_core/vulkan_common/vulkan_wrapper.h" +// Define all features which may be used by the implementation here. +// Vulkan version in the macro describes the minimum version required for feature availability. +// If the Vulkan version is lower than the required version, the named extension is required. +#define FOR_EACH_VK_FEATURE_1_1(FEATURE) \ + FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \ + FEATURE(KHR, 16BitStorage, 16BIT_STORAGE, bit16_storage) \ + FEATURE(KHR, ShaderAtomicInt64, SHADER_ATOMIC_INT64, shader_atomic_int64) \ + FEATURE(KHR, ShaderDrawParameters, SHADER_DRAW_PARAMETERS, shader_draw_parameters) \ + FEATURE(KHR, ShaderFloat16Int8, SHADER_FLOAT16_INT8, shader_float16_int8) \ + FEATURE(KHR, UniformBufferStandardLayout, UNIFORM_BUFFER_STANDARD_LAYOUT, \ + uniform_buffer_standard_layout) \ + FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer) + +#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \ + FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \ + FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \ + FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) + +#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ + FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ + shader_demote_to_helper_invocation) + +// Define all features which may be used by the implementation and require an extension here. +#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \ + FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ + FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ + FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \ + FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \ + FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \ + FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ + FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ + FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \ + primitive_topology_list_restart) \ + FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ + FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ + FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ + FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ + FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ + pipeline_executable_properties) \ + FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ + workgroup_memory_explicit_layout) + +// Define miscellaneous extensions which may be used by the implementation here. +#define FOR_EACH_VK_EXTENSION(EXTENSION) \ + EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \ + EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \ + EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \ + EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \ + EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \ + EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \ + EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ + EXTENSION(EXT, TOOLING_INFO, tooling_info) \ + EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ + EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ + EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ + EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \ + EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ + EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \ + EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \ + EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \ + EXTENSION(KHR, SWAPCHAIN, swapchain) \ + EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ + EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ + EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ + EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ + EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) + +#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \ + EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32) + +// Define extensions which must be supported. +#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \ + EXTENSION_NAME(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) \ + EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \ + EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) + +#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \ + EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) + +#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \ + EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME) + +// Define extensions where the absence of the extension may result in a degraded experience. +#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \ + EXTENSION_NAME(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME) \ + EXTENSION_NAME(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME) \ + EXTENSION_NAME(VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME) + +// Define features which must be supported. +#define FOR_EACH_VK_MANDATORY_FEATURE(FEATURE_NAME) \ + FEATURE_NAME(bit16_storage, storageBuffer16BitAccess) \ + FEATURE_NAME(bit16_storage, uniformAndStorageBuffer16BitAccess) \ + FEATURE_NAME(bit8_storage, storageBuffer8BitAccess) \ + FEATURE_NAME(bit8_storage, uniformAndStorageBuffer8BitAccess) \ + FEATURE_NAME(features, depthBiasClamp) \ + FEATURE_NAME(features, depthClamp) \ + FEATURE_NAME(features, drawIndirectFirstInstance) \ + FEATURE_NAME(features, dualSrcBlend) \ + FEATURE_NAME(features, fillModeNonSolid) \ + FEATURE_NAME(features, fragmentStoresAndAtomics) \ + FEATURE_NAME(features, geometryShader) \ + FEATURE_NAME(features, imageCubeArray) \ + FEATURE_NAME(features, independentBlend) \ + FEATURE_NAME(features, largePoints) \ + FEATURE_NAME(features, logicOp) \ + FEATURE_NAME(features, multiDrawIndirect) \ + FEATURE_NAME(features, multiViewport) \ + FEATURE_NAME(features, occlusionQueryPrecise) \ + FEATURE_NAME(features, robustBufferAccess) \ + FEATURE_NAME(features, samplerAnisotropy) \ + FEATURE_NAME(features, sampleRateShading) \ + FEATURE_NAME(features, shaderClipDistance) \ + FEATURE_NAME(features, shaderCullDistance) \ + FEATURE_NAME(features, shaderImageGatherExtended) \ + FEATURE_NAME(features, shaderStorageImageWriteWithoutFormat) \ + FEATURE_NAME(features, tessellationShader) \ + FEATURE_NAME(features, vertexPipelineStoresAndAtomics) \ + FEATURE_NAME(features, wideLines) \ + FEATURE_NAME(host_query_reset, hostQueryReset) \ + FEATURE_NAME(robustness2, nullDescriptor) \ + FEATURE_NAME(robustness2, robustBufferAccess2) \ + FEATURE_NAME(robustness2, robustImageAccess2) \ + FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \ + FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \ + FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ + FEATURE_NAME(variable_pointer, variablePointers) \ + FEATURE_NAME(variable_pointer, variablePointersStorageBuffer) + +// Define features where the absence of the feature may result in a degraded experience. +#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \ + FEATURE_NAME(custom_border_color, customBorderColors) \ + FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \ + FEATURE_NAME(index_type_uint8, indexTypeUint8) \ + FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \ + FEATURE_NAME(provoking_vertex, provokingVertexLast) \ + FEATURE_NAME(shader_float16_int8, shaderFloat16) \ + FEATURE_NAME(shader_float16_int8, shaderInt8) \ + FEATURE_NAME(transform_feedback, transformFeedback) \ + FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \ + FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState) + namespace Vulkan { class NsightAftermathTracker; @@ -88,67 +239,69 @@ public: /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers. u32 ApiVersion() const { - return properties.apiVersion; + return properties.properties.apiVersion; } /// Returns the current driver version provided in Vulkan-formatted version numbers. u32 GetDriverVersion() const { - return properties.driverVersion; + return properties.properties.driverVersion; } /// Returns the device name. std::string_view GetModelName() const { - return properties.deviceName; + return properties.properties.deviceName; } /// Returns the driver ID. VkDriverIdKHR GetDriverID() const { - return driver_id; + return properties.driver.driverID; } + bool ShouldBoostClocks() const; + /// Returns uniform buffer alignment requeriment. VkDeviceSize GetUniformBufferAlignment() const { - return properties.limits.minUniformBufferOffsetAlignment; + return properties.properties.limits.minUniformBufferOffsetAlignment; } /// Returns storage alignment requeriment. VkDeviceSize GetStorageBufferAlignment() const { - return properties.limits.minStorageBufferOffsetAlignment; + return properties.properties.limits.minStorageBufferOffsetAlignment; } /// Returns the maximum range for storage buffers. VkDeviceSize GetMaxStorageBufferRange() const { - return properties.limits.maxStorageBufferRange; + return properties.properties.limits.maxStorageBufferRange; } /// Returns the maximum size for push constants. VkDeviceSize GetMaxPushConstantsSize() const { - return properties.limits.maxPushConstantsSize; + return properties.properties.limits.maxPushConstantsSize; } /// Returns the maximum size for shared memory. u32 GetMaxComputeSharedMemorySize() const { - return properties.limits.maxComputeSharedMemorySize; + return properties.properties.limits.maxComputeSharedMemorySize; } /// Returns float control properties of the device. const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const { - return float_controls; + return properties.float_controls; } /// Returns true if ASTC is natively supported. bool IsOptimalAstcSupported() const { - return is_optimal_astc_supported; + return features.features.textureCompressionASTC_LDR; } /// Returns true if the device supports float16 natively. bool IsFloat16Supported() const { - return is_float16_supported; + return features.shader_float16_int8.shaderFloat16; } /// Returns true if the device supports int8 natively. bool IsInt8Supported() const { - return is_int8_supported; + return features.shader_float16_int8.shaderInt8; } /// Returns true if the device warp size can potentially be bigger than guest's warp size. @@ -158,32 +311,32 @@ public: /// Returns true if the device can be forced to use the guest warp size. bool IsGuestWarpSizeSupported(VkShaderStageFlagBits stage) const { - return guest_warp_stages & stage; + return properties.subgroup_size_control.requiredSubgroupSizeStages & stage; } /// Returns the maximum number of push descriptors. u32 MaxPushDescriptors() const { - return max_push_descriptors; + return properties.push_descriptor.maxPushDescriptors; } /// Returns true if formatless image load is supported. bool IsFormatlessImageLoadSupported() const { - return is_formatless_image_load_supported; + return features.features.shaderStorageImageReadWithoutFormat; } /// Returns true if shader int64 is supported. bool IsShaderInt64Supported() const { - return is_shader_int64_supported; + return features.features.shaderInt64; } /// Returns true if shader int16 is supported. bool IsShaderInt16Supported() const { - return is_shader_int16_supported; + return features.features.shaderInt16; } // Returns true if depth bounds is supported. bool IsDepthBoundsSupported() const { - return is_depth_bounds_supported; + return features.features.depthBounds; } /// Returns true when blitting from and to depth stencil images is supported. @@ -193,127 +346,151 @@ public: /// Returns true if the device supports VK_NV_viewport_swizzle. bool IsNvViewportSwizzleSupported() const { - return nv_viewport_swizzle; + return extensions.viewport_swizzle; } /// Returns true if the device supports VK_NV_viewport_array2. bool IsNvViewportArray2Supported() const { - return nv_viewport_array2; + return extensions.viewport_array2; } /// Returns true if the device supports VK_NV_geometry_shader_passthrough. bool IsNvGeometryShaderPassthroughSupported() const { - return nv_geometry_shader_passthrough; + return extensions.geometry_shader_passthrough; } /// Returns true if the device supports VK_KHR_uniform_buffer_standard_layout. bool IsKhrUniformBufferStandardLayoutSupported() const { - return khr_uniform_buffer_standard_layout; + return extensions.uniform_buffer_standard_layout; } /// Returns true if the device supports VK_KHR_push_descriptor. bool IsKhrPushDescriptorSupported() const { - return khr_push_descriptor; + return extensions.push_descriptor; } /// Returns true if VK_KHR_pipeline_executable_properties is enabled. bool IsKhrPipelineExecutablePropertiesEnabled() const { - return khr_pipeline_executable_properties; + return extensions.pipeline_executable_properties; } /// Returns true if VK_KHR_swapchain_mutable_format is enabled. bool IsKhrSwapchainMutableFormatEnabled() const { - return khr_swapchain_mutable_format; + return extensions.swapchain_mutable_format; } /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { - return khr_workgroup_memory_explicit_layout; + return extensions.workgroup_memory_explicit_layout; } /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. bool IsTopologyListPrimitiveRestartSupported() const { - return is_topology_list_restart_supported; + return features.primitive_topology_list_restart.primitiveTopologyListRestart; } /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. bool IsPatchListPrimitiveRestartSupported() const { - return is_patch_list_restart_supported; + return features.primitive_topology_list_restart.primitiveTopologyPatchListRestart; } /// Returns true if the device supports VK_EXT_index_type_uint8. bool IsExtIndexTypeUint8Supported() const { - return ext_index_type_uint8; + return extensions.index_type_uint8; } /// Returns true if the device supports VK_EXT_sampler_filter_minmax. bool IsExtSamplerFilterMinmaxSupported() const { - return ext_sampler_filter_minmax; + return extensions.sampler_filter_minmax; } /// Returns true if the device supports VK_EXT_depth_range_unrestricted. bool IsExtDepthRangeUnrestrictedSupported() const { - return ext_depth_range_unrestricted; + return extensions.depth_range_unrestricted; } /// Returns true if the device supports VK_EXT_depth_clip_control. bool IsExtDepthClipControlSupported() const { - return ext_depth_clip_control; + return extensions.depth_clip_control; } /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. bool IsExtShaderViewportIndexLayerSupported() const { - return ext_shader_viewport_index_layer; + return extensions.shader_viewport_index_layer; } /// Returns true if the device supports VK_EXT_subgroup_size_control. bool IsExtSubgroupSizeControlSupported() const { - return ext_subgroup_size_control; + return extensions.subgroup_size_control; } /// Returns true if the device supports VK_EXT_transform_feedback. bool IsExtTransformFeedbackSupported() const { - return ext_transform_feedback; + return extensions.transform_feedback; } /// Returns true if the device supports VK_EXT_custom_border_color. bool IsExtCustomBorderColorSupported() const { - return ext_custom_border_color; + return extensions.custom_border_color; } /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { - return ext_extended_dynamic_state; + return extensions.extended_dynamic_state; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state2. + bool IsExtExtendedDynamicState2Supported() const { + return extensions.extended_dynamic_state2; + } + + bool IsExtExtendedDynamicState2ExtrasSupported() const { + return features.extended_dynamic_state2.extendedDynamicState2LogicOp; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + bool IsExtExtendedDynamicState3Supported() const { + return extensions.extended_dynamic_state3; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + bool IsExtExtendedDynamicState3BlendingSupported() const { + return dynamic_state3_blending; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + bool IsExtExtendedDynamicState3EnablesSupported() const { + return dynamic_state3_enables; } /// Returns true if the device supports VK_EXT_line_rasterization. bool IsExtLineRasterizationSupported() const { - return ext_line_rasterization; + return extensions.line_rasterization; } /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state. bool IsExtVertexInputDynamicStateSupported() const { - return ext_vertex_input_dynamic_state; + return extensions.vertex_input_dynamic_state; } /// Returns true if the device supports VK_EXT_shader_stencil_export. bool IsExtShaderStencilExportSupported() const { - return ext_shader_stencil_export; + return extensions.shader_stencil_export; } /// Returns true if the device supports VK_EXT_conservative_rasterization. bool IsExtConservativeRasterizationSupported() const { - return ext_conservative_rasterization; + return extensions.conservative_rasterization; } /// Returns true if the device supports VK_EXT_provoking_vertex. bool IsExtProvokingVertexSupported() const { - return ext_provoking_vertex; + return extensions.provoking_vertex; } /// Returns true if the device supports VK_KHR_shader_atomic_int64. bool IsExtShaderAtomicInt64Supported() const { - return ext_shader_atomic_int64; + return extensions.shader_atomic_int64; } /// Returns the minimum supported version of SPIR-V. @@ -321,7 +498,7 @@ public: if (instance_version >= VK_API_VERSION_1_3) { return 0x00010600U; } - if (khr_spirv_1_4) { + if (extensions.spirv_1_4) { return 0x00010400U; } return 0x00010000U; @@ -339,11 +516,11 @@ public: /// Returns the vendor name reported from Vulkan. std::string_view GetVendorName() const { - return vendor_name; + return properties.driver.driverName; } /// Returns the list of available extensions. - const std::vector<std::string>& GetAvailableExtensions() const { + const std::set<std::string, std::less<>>& GetAvailableExtensions() const { return supported_extensions; } @@ -352,7 +529,7 @@ public: } bool CanReportMemoryUsage() const { - return ext_memory_budget; + return extensions.memory_budget; } u64 GetDeviceMemoryUsage() const; @@ -373,33 +550,30 @@ public: return must_emulate_bgr565; } + bool HasNullDescriptor() const { + return features.robustness2.nullDescriptor; + } + u32 GetMaxVertexInputAttributes() const { - return max_vertex_input_attributes; + return properties.properties.limits.maxVertexInputAttributes; } u32 GetMaxVertexInputBindings() const { - return max_vertex_input_bindings; + return properties.properties.limits.maxVertexInputBindings; } private: - /// Checks if the physical device is suitable. - void CheckSuitability(bool requires_swapchain) const; + /// Checks if the physical device is suitable and configures the object state + /// with all necessary info about its properties. + bool GetSuitability(bool requires_swapchain); - /// Loads extensions into a vector and stores available ones in this object. - std::vector<const char*> LoadExtensions(bool requires_surface); + // Remove extensions which have incomplete feature support. + void RemoveUnsuitableExtensions(); + void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name); /// Sets up queue families. void SetupFamilies(VkSurfaceKHR surface); - /// Sets up device features. - void SetupFeatures(); - - /// Sets up device properties. - void SetupProperties(); - - /// Collects telemetry information from the device. - void CollectTelemetryParameters(); - /// Collects information about attached tools. void CollectToolingInfo(); @@ -410,84 +584,93 @@ private: std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; /// Returns true if ASTC textures are natively supported. - bool IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const; + bool ComputeIsOptimalAstcSupported() const; /// Returns true if the device natively supports blitting depth stencil images. bool TestDepthStencilBlits() const; - VkInstance instance; ///< Vulkan instance. - vk::DeviceDispatch dld; ///< Device function pointers. - vk::PhysicalDevice physical; ///< Physical device. - VkPhysicalDeviceProperties properties; ///< Device properties. - VkPhysicalDeviceFloatControlsPropertiesKHR float_controls{}; ///< Float control properties. - vk::Device logical; ///< Logical device. - vk::Queue graphics_queue; ///< Main graphics queue. - vk::Queue present_queue; ///< Main present queue. - u32 instance_version{}; ///< Vulkan onstance version. - u32 graphics_family{}; ///< Main graphics queue family index. - u32 present_family{}; ///< Main present queue family index. - VkDriverIdKHR driver_id{}; ///< Driver ID. - VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. - u64 device_access_memory{}; ///< Total size of device local memory in bytes. - u32 max_push_descriptors{}; ///< Maximum number of push descriptors - u32 sets_per_pool{}; ///< Sets per Description Pool - bool is_optimal_astc_supported{}; ///< Support for native ASTC. - bool is_float16_supported{}; ///< Support for float16 arithmetic. - bool is_int8_supported{}; ///< Support for int8 arithmetic. - bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. - bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. - bool is_depth_bounds_supported{}; ///< Support for depth bounds. - bool is_shader_float64_supported{}; ///< Support for float64. - bool is_shader_int64_supported{}; ///< Support for int64. - bool is_shader_int16_supported{}; ///< Support for int16. - bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. - bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. - bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list - ///< topologies. - bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. - bool is_integrated{}; ///< Is GPU an iGPU. - bool is_virtual{}; ///< Is GPU a virtual GPU. - bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. - bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. - bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. - bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. - bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts. - bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. - bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. - bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. - bool khr_pipeline_executable_properties{}; ///< Support for executable properties. - bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. - bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. - bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. - bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control - bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. - bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. - bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. - bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control. - bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. - bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. - bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. - bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization. - bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state. - bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. - bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. - bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. - bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. - bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget. - bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. +private: + VkInstance instance; ///< Vulkan instance. + vk::DeviceDispatch dld; ///< Device function pointers. + vk::PhysicalDevice physical; ///< Physical device. + vk::Device logical; ///< Logical device. + vk::Queue graphics_queue; ///< Main graphics queue. + vk::Queue present_queue; ///< Main present queue. + u32 instance_version{}; ///< Vulkan instance version. + u32 graphics_family{}; ///< Main graphics queue family index. + u32 present_family{}; ///< Main present queue family index. + + struct Extensions { +#define EXTENSION(prefix, macro_name, var_name) bool var_name{}; +#define FEATURE(prefix, struct_name, macro_name, var_name) bool var_name{}; + + FOR_EACH_VK_FEATURE_1_1(FEATURE); + FOR_EACH_VK_FEATURE_1_2(FEATURE); + FOR_EACH_VK_FEATURE_1_3(FEATURE); + FOR_EACH_VK_FEATURE_EXT(FEATURE); + FOR_EACH_VK_EXTENSION(EXTENSION); + FOR_EACH_VK_EXTENSION_WIN32(EXTENSION); + +#undef EXTENSION +#undef FEATURE + }; + + struct Features { +#define FEATURE_CORE(prefix, struct_name, macro_name, var_name) \ + VkPhysicalDevice##struct_name##Features var_name{}; +#define FEATURE_EXT(prefix, struct_name, macro_name, var_name) \ + VkPhysicalDevice##struct_name##Features##prefix var_name{}; + + FOR_EACH_VK_FEATURE_1_1(FEATURE_CORE); + FOR_EACH_VK_FEATURE_1_2(FEATURE_CORE); + FOR_EACH_VK_FEATURE_1_3(FEATURE_CORE); + FOR_EACH_VK_FEATURE_EXT(FEATURE_EXT); + +#undef FEATURE_CORE +#undef FEATURE_EXT + + VkPhysicalDeviceFeatures features{}; + }; + + struct Properties { + VkPhysicalDeviceDriverProperties driver{}; + VkPhysicalDeviceFloatControlsProperties float_controls{}; + VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; + VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; + VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; + + VkPhysicalDeviceProperties properties{}; + }; + + Extensions extensions{}; + Features features{}; + Properties properties{}; + + VkPhysicalDeviceFeatures2 features2{}; + VkPhysicalDeviceProperties2 properties2{}; + + // Misc features + bool is_optimal_astc_supported{}; ///< Support for all guest ASTC formats. + bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. + bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. + bool is_integrated{}; ///< Is GPU an iGPU. + bool is_virtual{}; ///< Is GPU a virtual GPU. + bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit bool has_renderdoc{}; ///< Has RenderDoc attached bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. - u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline - u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline + bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3. + bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3. + u64 device_access_memory{}; ///< Total size of device local memory in bytes. + u32 sets_per_pool{}; ///< Sets per Description Pool // Telemetry parameters - std::string vendor_name; ///< Device's driver name. - std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. - std::vector<size_t> valid_heap_memory; ///< Heaps used. + std::set<std::string, std::less<>> supported_extensions; ///< Reported Vulkan extensions. + std::set<std::string, std::less<>> loaded_extensions; ///< Loaded Vulkan extensions. + std::vector<size_t> valid_heap_memory; ///< Heaps used. /// Format properties dictionary. std::unordered_map<VkFormat, VkFormatProperties> format_properties; diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 562039b56..b6d83e446 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -32,7 +32,7 @@ namespace Vulkan { namespace { [[nodiscard]] std::vector<const char*> RequiredExtensions( - Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) { + Core::Frontend::WindowSystemType window_type, bool enable_validation) { std::vector<const char*> extensions; extensions.reserve(6); switch (window_type) { @@ -65,7 +65,7 @@ namespace { if (window_type != Core::Frontend::WindowSystemType::Headless) { extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); } - if (enable_debug_utils) { + if (enable_validation) { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); @@ -95,9 +95,9 @@ namespace { return true; } -[[nodiscard]] std::vector<const char*> Layers(bool enable_layers) { +[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) { std::vector<const char*> layers; - if (enable_layers) { + if (enable_validation) { layers.push_back("VK_LAYER_KHRONOS_validation"); } return layers; @@ -125,7 +125,7 @@ void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, Core::Frontend::WindowSystemType window_type, - bool enable_debug_utils, bool enable_layers) { + bool enable_validation) { if (!library.IsOpen()) { LOG_ERROR(Render_Vulkan, "Vulkan library not available"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); @@ -138,11 +138,11 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } - const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); + const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation); if (!AreExtensionsSupported(dld, extensions)) { throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } - std::vector<const char*> layers = Layers(enable_layers); + std::vector<const char*> layers = Layers(enable_validation); RemoveUnavailableLayers(dld, layers); const u32 available_version = vk::AvailableVersion(dld); diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h index 40419d802..b59b92f83 100644 --- a/src/video_core/vulkan_common/vulkan_instance.h +++ b/src/video_core/vulkan_common/vulkan_instance.h @@ -17,8 +17,7 @@ namespace Vulkan { * @param dld Dispatch table to load function pointers into * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1) * @param window_type Window system type's enabled extension - * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not - * @param enable_layers Whether to enable Vulkan validation layers or not + * @param enable_validation Whether to enable Vulkan validation layers or not * * @return A new Vulkan instance * @throw vk::Exception on failure @@ -26,6 +25,6 @@ namespace Vulkan { [[nodiscard]] vk::Instance CreateInstance( const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless, - bool enable_debug_utils = false, bool enable_layers = false); + bool enable_validation = false); } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 7dca7341c..486d4dfaf 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -94,6 +94,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdDispatch); X(vkCmdDraw); X(vkCmdDrawIndexed); + X(vkCmdDrawIndirect); + X(vkCmdDrawIndexedIndirect); + X(vkCmdDrawIndirectCount); + X(vkCmdDrawIndexedIndirectCount); X(vkCmdEndQuery); X(vkCmdEndRenderPass); X(vkCmdEndTransformFeedbackEXT); @@ -118,12 +122,22 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetDepthCompareOpEXT); X(vkCmdSetDepthTestEnableEXT); X(vkCmdSetDepthWriteEnableEXT); + X(vkCmdSetPrimitiveRestartEnableEXT); + X(vkCmdSetRasterizerDiscardEnableEXT); + X(vkCmdSetDepthBiasEnableEXT); + X(vkCmdSetLogicOpEnableEXT); + X(vkCmdSetDepthClampEnableEXT); X(vkCmdSetFrontFaceEXT); + X(vkCmdSetLogicOpEXT); + X(vkCmdSetPatchControlPointsEXT); X(vkCmdSetLineWidth); X(vkCmdSetPrimitiveTopologyEXT); X(vkCmdSetStencilOpEXT); X(vkCmdSetStencilTestEnableEXT); X(vkCmdSetVertexInputEXT); + X(vkCmdSetColorWriteMaskEXT); + X(vkCmdSetColorBlendEnableEXT); + X(vkCmdSetColorBlendEquationEXT); X(vkCmdResolveImage); X(vkCreateBuffer); X(vkCreateBufferView); @@ -138,6 +152,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreateGraphicsPipelines); X(vkCreateImage); X(vkCreateImageView); + X(vkCreatePipelineCache); X(vkCreatePipelineLayout); X(vkCreateQueryPool); X(vkCreateRenderPass); @@ -157,6 +172,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkDestroyImage); X(vkDestroyImageView); X(vkDestroyPipeline); + X(vkDestroyPipelineCache); X(vkDestroyPipelineLayout); X(vkDestroyQueryPool); X(vkDestroyRenderPass); @@ -174,6 +190,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetEventStatus); X(vkGetFenceStatus); X(vkGetImageMemoryRequirements); + X(vkGetPipelineCacheData); X(vkGetMemoryFdKHR); #ifdef _WIN32 X(vkGetMemoryWin32HandleKHR); @@ -204,6 +221,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { if (!dld.vkResetQueryPool) { Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device); } + + // Support for draw indirect with count is optional in Vulkan 1.2 + if (!dld.vkCmdDrawIndirectCount) { + Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device); + Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device); + } #undef X } @@ -417,6 +440,10 @@ void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noex dld.vkDestroyPipeline(device, handle, nullptr); } +void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyPipelineCache(device, handle, nullptr); +} + void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { dld.vkDestroyPipelineLayout(device, handle, nullptr); } @@ -637,6 +664,10 @@ void ShaderModule::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); } +void PipelineCache::SetObjectNameEXT(const char* name) const { + SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name); +} + void Semaphore::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name); } @@ -732,21 +763,29 @@ DescriptorSetLayout Device::CreateDescriptorSetLayout( return DescriptorSetLayout(object, handle, *dld); } +PipelineCache Device::CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const { + VkPipelineCache cache; + Check(dld->vkCreatePipelineCache(handle, &ci, nullptr, &cache)); + return PipelineCache(cache, handle, *dld); +} + PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { VkPipelineLayout object; Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); return PipelineLayout(object, handle, *dld); } -Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { +Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, + VkPipelineCache cache) const { VkPipeline object; - Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); + Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object)); return Pipeline(object, handle, *dld); } -Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { +Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci, + VkPipelineCache cache) const { VkPipeline object; - Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); + Check(dld->vkCreateComputePipelines(handle, cache, 1, &ci, nullptr, &object)); return Pipeline(object, handle, *dld); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 8bd4fd4d9..e86f661cb 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -213,6 +213,10 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdDispatch vkCmdDispatch{}; PFN_vkCmdDraw vkCmdDraw{}; PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; + PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; + PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{}; + PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{}; + PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{}; PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; PFN_vkCmdEndQuery vkCmdEndQuery{}; PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; @@ -230,8 +234,15 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{}; PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{}; PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; + PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{}; + PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{}; + PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{}; + PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{}; + PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{}; PFN_vkCmdSetEvent vkCmdSetEvent{}; PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; + PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{}; + PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{}; PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; PFN_vkCmdSetScissor vkCmdSetScissor{}; @@ -242,6 +253,9 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; PFN_vkCmdSetViewport vkCmdSetViewport{}; + PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{}; + PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; + PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; @@ -256,6 +270,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; PFN_vkCreateImage vkCreateImage{}; PFN_vkCreateImageView vkCreateImageView{}; + PFN_vkCreatePipelineCache vkCreatePipelineCache{}; PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateRenderPass vkCreateRenderPass{}; @@ -275,6 +290,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkDestroyImage vkDestroyImage{}; PFN_vkDestroyImageView vkDestroyImageView{}; PFN_vkDestroyPipeline vkDestroyPipeline{}; + PFN_vkDestroyPipelineCache vkDestroyPipelineCache{}; PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; PFN_vkDestroyQueryPool vkDestroyQueryPool{}; PFN_vkDestroyRenderPass vkDestroyRenderPass{}; @@ -292,6 +308,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetEventStatus vkGetEventStatus{}; PFN_vkGetFenceStatus vkGetFenceStatus{}; PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; + PFN_vkGetPipelineCacheData vkGetPipelineCacheData{}; PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; #ifdef _WIN32 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; @@ -337,6 +354,7 @@ void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; @@ -759,6 +777,18 @@ public: void SetObjectNameEXT(const char* name) const; }; +class PipelineCache : public Handle<VkPipelineCache, VkDevice, DeviceDispatch> { + using Handle<VkPipelineCache, VkDevice, DeviceDispatch>::Handle; + +public: + /// Set object name. + void SetObjectNameEXT(const char* name) const; + + VkResult Read(size_t* size, void* data) const noexcept { + return dld->vkGetPipelineCacheData(owner, handle, size, data); + } +}; + class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle; @@ -830,11 +860,15 @@ public: DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; + PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const; + PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; - Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; + Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; - Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; + Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; @@ -1019,6 +1053,29 @@ public: first_instance); } + void DrawIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count, + u32 stride) const noexcept { + dld->vkCmdDrawIndirect(handle, src_buffer, src_offset, draw_count, stride); + } + + void DrawIndexedIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count, + u32 stride) const noexcept { + dld->vkCmdDrawIndexedIndirect(handle, src_buffer, src_offset, draw_count, stride); + } + + void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer, + VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept { + dld->vkCmdDrawIndirectCount(handle, src_buffer, src_offset, count_buffer, count_offset, + draw_count, stride); + } + + void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, + VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count, + u32 stride) const noexcept { + dld->vkCmdDrawIndexedIndirectCount(handle, src_buffer, src_offset, count_buffer, + count_offset, draw_count, stride); + } + void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const noexcept { dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), @@ -1192,10 +1249,51 @@ public: dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); } + void SetPrimitiveRestartEnableEXT(bool enable) const noexcept { + dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } + + void SetRasterizerDiscardEnableEXT(bool enable) const noexcept { + dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } + + void SetDepthBiasEnableEXT(bool enable) const noexcept { + dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } + + void SetLogicOpEnableEXT(bool enable) const noexcept { + dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } + + void SetDepthClampEnableEXT(bool enable) const noexcept { + dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } + void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { dld->vkCmdSetFrontFaceEXT(handle, front_face); } + void SetLogicOpEXT(VkLogicOp logic_op) const noexcept { + dld->vkCmdSetLogicOpEXT(handle, logic_op); + } + + void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept { + dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points); + } + + void SetColorWriteMaskEXT(u32 first, Span<VkColorComponentFlags> masks) const noexcept { + dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data()); + } + + void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept { + dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data()); + } + + void SetColorBlendEquationEXT(u32 first, + Span<VkColorBlendEquationEXT> equations) const noexcept { + dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data()); + } + void SetLineWidth(float line_width) const noexcept { dld->vkCmdSetLineWidth(handle, line_width); } |