summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/CMakeLists.txt10
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h96
-rw-r--r--src/video_core/cache_types.h24
-rw-r--r--src/video_core/dma_pusher.cpp42
-rw-r--r--src/video_core/dma_pusher.h2
-rw-r--r--src/video_core/engines/draw_manager.cpp62
-rw-r--r--src/video_core/engines/draw_manager.h45
-rw-r--r--src/video_core/engines/engine_interface.h24
-rw-r--r--src/video_core/engines/engine_upload.cpp2
-rw-r--r--src/video_core/engines/fermi_2d.cpp16
-rw-r--r--src/video_core/engines/fermi_2d.h3
-rw-r--r--src/video_core/engines/kepler_compute.cpp14
-rw-r--r--src/video_core/engines/kepler_compute.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp183
-rw-r--r--src/video_core/engines/maxwell_3d.h91
-rw-r--r--src/video_core/engines/maxwell_dma.cpp33
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/fsr.cpp148
-rw-r--r--src/video_core/fsr.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt35
-rw-r--r--src/video_core/host_shaders/blit_color_float.frag (renamed from src/video_core/host_shaders/vulkan_blit_color_float.frag)0
-rw-r--r--src/video_core/host_shaders/full_screen_triangle.vert13
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag108
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag9
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag9
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.vert10
-rw-r--r--src/video_core/host_shaders/vulkan_quad_indexed.comp6
-rw-r--r--src/video_core/host_shaders/vulkan_turbo_mode.comp29
-rw-r--r--src/video_core/invalidation_accumulator.h79
-rw-r--r--src/video_core/macro/macro.cpp19
-rw-r--r--src/video_core/macro/macro.h1
-rw-r--r--src/video_core/macro/macro_hle.cpp606
-rw-r--r--src/video_core/macro/macro_hle.h5
-rw-r--r--src/video_core/memory_manager.cpp180
-rw-r--r--src/video_core/memory_manager.h54
-rw-r--r--src/video_core/rasterizer_interface.h30
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp9
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h13
-rw-r--r--src/video_core/renderer_opengl/blit_image.cpp59
-rw-r--r--src/video_core/renderer_opengl/blit_image.h38
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp232
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h21
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp121
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h129
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp98
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp212
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h16
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp119
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h76
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp23
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp29
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp318
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp42
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp144
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp127
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp161
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp493
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h26
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp56
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp102
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h119
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.cpp222
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.h35
-rw-r--r--src/video_core/shader_environment.cpp58
-rw-r--r--src/video_core/shader_environment.h21
-rw-r--r--src/video_core/texture_cache/descriptor_table.h4
-rw-r--r--src/video_core/texture_cache/image_info.cpp4
-rw-r--r--src/video_core/texture_cache/samples_helper.h44
-rw-r--r--src/video_core/texture_cache/slot_vector.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h145
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h17
-rw-r--r--src/video_core/texture_cache/util.cpp20
-rw-r--r--src/video_core/texture_cache/util.h5
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1284
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h441
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.h5
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp47
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h102
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, &macro_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(&params);
+ PrepareDraw(params.is_indexed, [this, &params](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(&params);
+ PrepareDraw(params.is_indexed, [this, &params] {
+ 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);
}