aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp30
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h8
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp155
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp137
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp50
18 files changed, 336 insertions, 113 deletions
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 866b721a84..d53cf70b37 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -190,9 +190,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
}
}
}
- if (!extended_dynamic_state_3_enables) {
- dynamic_state.Refresh3(regs);
- }
+ dynamic_state.Refresh3(regs, features);
if (xfb_enabled) {
RefreshXfbState(xfb_state, regs);
}
@@ -295,16 +293,22 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
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);
-
- line_stipple_enable.Assign(regs.line_stipple_enable);
+void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs,
+ const DynamicFeatures& features) {
+ if (!features.has_dynamic_state3_logic_op_enable) {
+ logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
+ }
+ if (!features.has_dynamic_state3_depth_clamp_enable) {
+ 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);
+ }
+ if (!features.has_dynamic_state3_line_stipple_enable) {
+ line_stipple_enable.Assign(regs.line_stipple_enable);
+ }
}
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 ffc91e9a55..44157d686d 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -27,6 +27,9 @@ struct DynamicFeatures {
bool has_extended_dynamic_state_2_patch_control_points;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
+ bool has_dynamic_state3_depth_clamp_enable;
+ bool has_dynamic_state3_logic_op_enable;
+ bool has_dynamic_state3_line_stipple_enable;
bool has_dynamic_vertex_input;
bool has_provoking_vertex;
bool has_provoking_vertex_first_mode;
@@ -175,7 +178,7 @@ struct FixedPipelineState {
void Refresh(const Maxwell& regs);
void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
bool base_features_supported);
- void Refresh3(const Maxwell& regs);
+ void Refresh3(const Maxwell& regs, const DynamicFeatures& features);
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func);
@@ -265,8 +268,7 @@ struct FixedPipelineState {
return sizeof(*this);
}
if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
- // Exclude dynamic state and attributes
- return offsetof(FixedPipelineState, dynamic_state);
+ return offsetof(FixedPipelineState, attachments);
}
if (dynamic_vertex_input) {
// Exclude dynamic state
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 010cfd225d..2deec13ace 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -8,6 +8,7 @@
#include <array>
#include <cstring>
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <vector>
@@ -171,7 +172,11 @@ try
RendererVulkan::~RendererVulkan() {
scheduler.RegisterOnSubmit([] {});
- void(device.GetLogical().WaitIdle());
+ scheduler.Finish();
+ {
+ std::scoped_lock lock{scheduler.submit_mutex};
+ void(device.GetLogical().WaitIdle());
+ }
}
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index bb7eb9bdaa..75a8c3bf91 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -8,6 +8,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <vulkan/vulkan_core.h>
+#include <mutex>
#include "video_core/framebuffer_config.h"
#include "video_core/present.h"
#include "video_core/renderer_vulkan/present/filters.h"
@@ -31,7 +32,10 @@ BlitScreen::~BlitScreen() = default;
void BlitScreen::WaitIdle() {
present_manager.WaitPresent();
scheduler.Finish();
- device.GetLogical().WaitIdle();
+ {
+ std::scoped_lock lock{scheduler.submit_mutex};
+ device.GetLogical().WaitIdle();
+ }
}
void BlitScreen::SetWindowAdaptPass() {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 74f06427dd..a359502046 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -637,12 +637,10 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
auto handle = bindings.buffers[i]->Handle();
if (handle == VK_NULL_HANDLE) {
+ ReserveNullBuffer();
+ handle = *null_buffer;
bindings.offsets[i] = 0;
- bindings.sizes[i] = VK_WHOLE_SIZE;
- if (!device.HasNullDescriptor()) {
- ReserveNullBuffer();
- handle = *null_buffer;
- }
+ bindings.sizes[i] = 0;
}
buffer_handles[i] = handle;
}
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index d45a57f7bb..f198b65d69 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -228,6 +228,10 @@ struct QueriesPrefixScanPushConstants {
u32 accumulation_limit;
u32 buffer_offset;
};
+
+struct ConditionalRenderingResolvePushConstants {
+ u32 compare_to_zero;
+};
} // Anonymous namespace
ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
@@ -413,7 +417,8 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS,
- INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, nullptr,
+ INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO,
+ COMPUTE_PUSH_CONSTANT_RANGE<sizeof(ConditionalRenderingResolvePushConstants)>,
RESOLVE_CONDITIONAL_RENDER_COMP_SPV),
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
@@ -430,7 +435,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([this, descriptor_data](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
@@ -443,6 +448,9 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT,
};
+ const ConditionalRenderingResolvePushConstants uniforms{
+ .compare_to_zero = compare_to_zero ? 1U : 0U,
+ };
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
@@ -450,9 +458,11 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
+ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(1, 1, 1);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier);
+ VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0,
+ write_barrier);
});
}
@@ -520,7 +530,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 8f706a02c8..43fbefe425 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -467,6 +467,10 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
bind_stage_info(4);
}
+ if (regs.transform_feedback_enabled != 0) {
+ scheduler.RequestOutsideRenderPassOperationContext();
+ }
+
buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed);
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 5f86506961..7b5ac7fac0 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -485,6 +485,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
device.IsExtExtendedDynamicState3BlendingSupported();
dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported();
+ dynamic_features.has_dynamic_state3_depth_clamp_enable =
+ device.SupportsDynamicState3DepthClampEnable();
+ dynamic_features.has_dynamic_state3_logic_op_enable =
+ device.SupportsDynamicState3LogicOpEnable();
+ dynamic_features.has_dynamic_state3_line_stipple_enable =
+ device.SupportsDynamicState3LineStippleEnable();
// VIDS: Independent toggle (not affected by dyna_state levels)
dynamic_features.has_dynamic_vertex_input =
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 80853362ad..de854554c7 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -189,7 +189,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
frame->image = memory_allocator.CreateImage({
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
- .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
+ .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT,
.imageType = VK_IMAGE_TYPE_2D,
.format = swapchain.GetImageFormat(),
.extent =
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 8518d89eee..6aea5d18a8 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -157,8 +157,9 @@ public:
ReserveHostQuery();
scheduler.Record([query_pool = current_query_pool,
- query_index = current_bank_slot](vk::CommandBuffer cmdbuf) {
+ query_index = current_bank_slot](vk::CommandBuffer cmdbuf) {
const bool use_precise = Settings::IsGPULevelHigh();
+ cmdbuf.ResetQueryPool(query_pool, static_cast<u32>(query_index), 1);
cmdbuf.BeginQuery(query_pool, static_cast<u32>(query_index),
use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
});
@@ -220,8 +221,7 @@ public:
}
PauseCounter();
const auto driver_id = device.GetDriverID();
- if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
- driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
+ if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
pending_sync.clear();
sync_values_stash.clear();
return;
@@ -666,13 +666,18 @@ public:
offsets.fill(0);
last_queries.fill(0);
last_queries_stride.fill(1);
+ stream_to_slot.fill(INVALID_SLOT);
+ VkBufferUsageFlags counter_buffer_usage =
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ if (device.IsExtTransformFeedbackSupported()) {
+ counter_buffer_usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
+ }
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS,
- .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
- VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT,
+ .usage = counter_buffer_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -692,6 +697,9 @@ public:
~TFBCounterStreamer() = default;
void StartCounter() override {
+ if (!device.IsExtTransformFeedbackSupported()) {
+ return;
+ }
FlushBeginTFB();
has_started = true;
}
@@ -706,7 +714,9 @@ public:
void CloseCounter() override {
if (has_flushed_end_pending) {
- FlushEndTFB();
+ if (scheduler.IsRenderPassActive()) {
+ FlushEndTFB();
+ }
}
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
if (maxwell3d.regs.transform_feedback_enabled == 0) {
@@ -756,18 +766,33 @@ public:
if (has_timestamp) {
new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp;
}
+ if (!device.IsExtTransformFeedbackSupported()) {
+ new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
+ return index;
+ }
if (!subreport_) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
const size_t subreport = static_cast<size_t>(*subreport_);
+ if (subreport >= NUM_STREAMS) {
+ new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
+ return index;
+ }
last_queries[subreport] = address;
if ((streams_mask & (1ULL << subreport)) == 0) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
+ const size_t slot = stream_to_slot[subreport];
+ if (slot >= NUM_STREAMS) {
+ new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
+ return index;
+ }
+
+ scheduler.RequestOutsideRenderPassOperationContext();
CloseCounter();
- auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport);
+ auto [bank_slot, data_slot] = ProduceCounterBuffer(slot);
new_query->start_bank_id = static_cast<u32>(bank_slot);
new_query->size_banks = 1;
new_query->start_slot = static_cast<u32>(data_slot);
@@ -778,6 +803,9 @@ public:
}
std::optional<std::pair<DAddr, size_t>> GetLastQueryStream(size_t stream) {
+ if (stream >= NUM_STREAMS) {
+ return std::nullopt;
+ }
if (last_queries[stream] != 0) {
std::pair<DAddr, size_t> result(last_queries[stream], last_queries_stride[stream]);
return result;
@@ -789,6 +817,10 @@ public:
return out_topology;
}
+ u32 GetPatchVertices() const {
+ return patch_vertices;
+ }
+
bool HasUnsyncedQueries() const override {
return !pending_flush_queries.empty();
}
@@ -855,6 +887,9 @@ public:
private:
void FlushBeginTFB() {
+ if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
+ return;
+ }
if (has_flushed_end_pending) [[unlikely]] {
return;
}
@@ -868,12 +903,24 @@ private:
});
return;
}
+ static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{
+ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
+ .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
+ };
scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) {
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
+ VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0,
+ COUNTER_RESUME_BARRIER);
cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data());
});
}
void FlushEndTFB() {
+ if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
+ return;
+ }
if (!has_flushed_end_pending) [[unlikely]] {
UNREACHABLE();
return;
@@ -899,28 +946,48 @@ private:
void UpdateBuffers() {
last_queries.fill(0);
last_queries_stride.fill(1);
+ stream_to_slot.fill(INVALID_SLOT);
streams_mask = 0; // reset previously recorded streams
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0;
out_topology = maxwell3d.draw_manager->GetDrawState().topology;
+ patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
+ if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) {
+ switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) {
+ case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points:
+ out_topology = Maxwell3D::Regs::PrimitiveTopology::Points;
+ break;
+ case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines:
+ out_topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
+ break;
+ case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW:
+ case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW:
+ out_topology = Maxwell3D::Regs::PrimitiveTopology::TriangleStrip;
+ break;
+ }
+ }
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
continue;
}
+ buffers_count = std::max<size_t>(buffers_count, i + 1);
const size_t stream = tf.controls[i].stream;
if (stream >= last_queries_stride.size()) {
LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream);
continue;
}
+ if ((streams_mask & (1ULL << stream)) != 0) {
+ continue;
+ }
last_queries_stride[stream] = tf.controls[i].stride;
+ stream_to_slot[stream] = i;
streams_mask |= 1ULL << stream;
- buffers_count = std::max<size_t>(buffers_count, stream + 1);
}
});
}
- std::pair<size_t, size_t> ProduceCounterBuffer(size_t stream) {
+ std::pair<size_t, size_t> ProduceCounterBuffer(size_t slot_index) {
if (current_bank == nullptr || current_bank->IsClosed()) {
current_bank_id =
bank_pool.ReserveBank([this](std::deque<TFBQueryBank>& queue, size_t index) {
@@ -946,7 +1013,8 @@ private:
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_buffer = current_bank->GetBuffer(),
- src_buffer = counter_buffers[stream], src_offset = offsets[stream],
+ src_buffer = counter_buffers[slot_index],
+ src_offset = offsets[slot_index],
slot](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER);
@@ -965,6 +1033,7 @@ private:
friend class PrimitivesSucceededStreamer;
static constexpr size_t NUM_STREAMS = 4;
+ static constexpr size_t INVALID_SLOT = NUM_STREAMS;
QueryCacheRuntime& runtime;
const Device& device;
@@ -994,7 +1063,9 @@ private:
std::array<VkDeviceSize, NUM_STREAMS> offsets{};
std::array<DAddr, NUM_STREAMS> last_queries;
std::array<size_t, NUM_STREAMS> last_queries_stride;
+ std::array<size_t, NUM_STREAMS> stream_to_slot;
Maxwell3D::Regs::PrimitiveTopology out_topology;
+ u32 patch_vertices{1};
u64 streams_mask;
};
@@ -1015,6 +1086,7 @@ public:
u64 stride{};
DAddr dependant_address{};
Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points};
+ u32 patch_vertices{1};
size_t dependant_index{};
bool dependant_manage{};
};
@@ -1031,6 +1103,10 @@ public:
~PrimitivesSucceededStreamer() = default;
+ void ResetCounter() override {
+ tfb_streamer.ResetCounter();
+ }
+
size_t WriteCounter(DAddr address, bool has_timestamp, u32 value,
std::optional<u32> subreport_) override {
auto index = BuildQuery();
@@ -1048,6 +1124,7 @@ public:
auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport);
bool must_manage_dependance = false;
new_query->topology = tfb_streamer.GetOutputTopology();
+ new_query->patch_vertices = tfb_streamer.GetPatchVertices();
if (dependant_address_opt) {
auto [dep_address, stride] = *dependant_address_opt;
new_query->dependant_address = dep_address;
@@ -1068,6 +1145,7 @@ public:
}
new_query->stride = 1;
runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) {
+ new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
@@ -1131,27 +1209,39 @@ public:
}
}
query->value = [&]() -> u64 {
+ const auto saturating_subtract = [](u64 value, u64 amount) {
+ return value > amount ? value - amount : 0;
+ };
switch (query->topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points:
return num_vertices;
case Maxwell3D::Regs::PrimitiveTopology::Lines:
return num_vertices / 2;
case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
- return (num_vertices / 2) + 1;
+ return num_vertices > 1 ? num_vertices : 0;
case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
- return num_vertices - 1;
- case Maxwell3D::Regs::PrimitiveTopology::Patches:
+ return saturating_subtract(num_vertices, 1);
+ case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
+ return num_vertices / 4;
+ case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
+ return saturating_subtract(num_vertices, 3);
case Maxwell3D::Regs::PrimitiveTopology::Triangles:
- case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 3;
+ case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
+ return num_vertices / 6;
case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
+ return saturating_subtract(num_vertices, 2);
case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
- return num_vertices - 2;
+ return num_vertices > 4 ? (num_vertices - 4) / 2 : 0;
case Maxwell3D::Regs::PrimitiveTopology::Quads:
return num_vertices / 4;
+ case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
+ return num_vertices > 2 ? (num_vertices - 2) / 2 : 0;
case Maxwell3D::Regs::PrimitiveTopology::Polygon:
- return 1U;
+ return num_vertices >= 3 ? 1U : 0U;
+ case Maxwell3D::Regs::PrimitiveTopology::Patches:
+ return num_vertices / std::max<u64>(query->patch_vertices, 1U);
default:
return num_vertices;
}
@@ -1202,16 +1292,24 @@ struct QueryCacheRuntimeImpl {
hcr_setup.pNext = nullptr;
hcr_setup.flags = 0;
- conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
- device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
+ const bool has_conditional_rendering = device.IsExtConditionalRendering();
+ if (has_conditional_rendering) {
+ conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
+ device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
+ }
+
+ VkBufferUsageFlags hcr_buffer_usage =
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ if (has_conditional_rendering) {
+ hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
+ }
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = sizeof(u32),
- .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
- VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
+ .usage = hcr_buffer_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -1338,15 +1436,17 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
}
}
-void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) {
+void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
+ bool compare_to_zero) {
VkBuffer to_resolve;
u32 to_resolve_offset;
+ const u32 resolve_size = compare_to_zero ? 8 : 24;
{
std::scoped_lock lk(impl->buffer_cache.mutex);
- static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::NoSynchronize;
+ const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing;
const auto [buffer, offset] =
- impl->buffer_cache.ObtainCPUBuffer(address, 24, sync_info, post_op);
+ impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op);
to_resolve = buffer->Handle();
to_resolve_offset = static_cast<u32>(offset);
}
@@ -1355,7 +1455,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
PauseHostConditionalRendering();
}
impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve,
- to_resolve_offset, false);
+ to_resolve_offset, compare_to_zero);
impl->hcr_setup.buffer = *impl->hcr_resolve_buffer;
impl->hcr_setup.offset = 0;
impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
@@ -1371,7 +1471,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup
if (!impl->device.IsExtConditionalRendering()) {
return false;
}
- HostConditionalRenderingCompareValueImpl(object_1, false);
+ HostConditionalRenderingCompareBCImpl(object_1.address, true, true);
return true;
}
@@ -1420,7 +1520,8 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh();
- if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
+ if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
+ EndHostConditionalRendering();
return true;
}
@@ -1437,10 +1538,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
}
if (!is_gpu_high) {
+ EndHostConditionalRendering();
return true;
}
if (!is_in_bc[0] && !is_in_bc[1]) {
+ EndHostConditionalRendering();
return true;
}
HostConditionalRenderingCompareBCImpl(object_1.address, equal_check);
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index e2aa4d991e..bbb5234e11 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -63,7 +63,8 @@ public:
private:
void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal);
- void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal);
+ void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
+ bool compare_to_zero = false);
friend struct QueryCacheRuntimeImpl;
std::unique_ptr<QueryCacheRuntimeImpl> impl;
};
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ba7b5d8c1b..78337b3ebe 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -173,6 +173,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
}
return params;
}
+
+bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
+ switch (topology) {
+ case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
+ case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
+ case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
+ case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
+ case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
+ case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) {
+ return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
+ device.IsTopologyListPrimitiveRestartSupported()) ||
+ SupportsPrimitiveRestart(topology) ||
+ (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
+ device.IsPatchListPrimitiveRestartSupported()));
+}
} // Anonymous namespace
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -225,6 +247,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates();
+ query_cache.NotifySegment(true);
HandleTransformFeedback();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
@@ -336,6 +359,7 @@ void RasterizerVulkan::DrawTexture() {
UpdateDynamicStates();
+ query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
@@ -575,11 +599,17 @@ void RasterizerVulkan::DispatchCompute() {
}
void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) {
- if (type != VideoCommon::QueryType::ZPassPixelCount64) {
+ switch (type) {
+ case VideoCommon::QueryType::ZPassPixelCount64:
+ case VideoCommon::QueryType::StreamingByteCount:
+ case VideoCommon::QueryType::StreamingPrimitivesSucceeded:
+ case VideoCommon::QueryType::VtgPrimitivesOut:
+ query_cache.CounterReset(type);
+ return;
+ default:
LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type);
return;
}
- query_cache.CounterReset(type);
}
void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
@@ -766,6 +796,9 @@ void RasterizerVulkan::ReleaseFences(bool force) {
void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size,
VideoCommon::CacheType which) {
+ if (Settings::IsGPULevelHigh()) {
+ FlushRegion(addr, size, which);
+ }
InvalidateRegion(addr, size, which);
}
@@ -830,6 +863,10 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
return query_cache.AccelerateHostConditionalRendering();
}
+bool RasterizerVulkan::HasDrawTransformFeedback() {
+ return device.IsTransformFeedbackDrawSupported();
+}
+
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -976,6 +1013,12 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
void RasterizerVulkan::UpdateDynamicStates() {
auto& regs = maxwell3d->regs;
+ auto& flags = maxwell3d->dirty.flags;
+ const auto topology = maxwell3d->draw_manager->GetDrawState().topology;
+ if (state_tracker.ChangePrimitiveTopology(topology)) {
+ flags[Dirty::DepthBiasEnable] = true;
+ flags[Dirty::PrimitiveRestartEnable] = true;
+ }
// Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting
UpdateViewportsState(regs);
@@ -1084,6 +1127,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
if (!state_tracker.TouchViewports()) {
return;
}
+
+ maxwell3d->dirty.flags[Dirty::Scissors] = true;
+
if (!regs.viewport_scale_offset_enabled) {
float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y);
@@ -1101,8 +1147,12 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
- scheduler.Record([viewport](vk::CommandBuffer cmdbuf) {
- cmdbuf.SetViewport(0, viewport);
+ scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) {
+ const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
+ std::array<VkViewport, Maxwell::NumViewports> viewport_list{};
+ viewport_list.fill(viewport);
+ const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
+ cmdbuf.SetViewport(0, viewports);
});
return;
}
@@ -1142,8 +1192,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width;
scissor.extent.height = height;
- scheduler.Record([scissor](vk::CommandBuffer cmdbuf) {
- cmdbuf.SetScissor(0, scissor);
+ scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) {
+ const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
+ std::array<VkRect2D, Maxwell::NumViewports> scissor_list{};
+ scissor_list.fill(scissor);
+ const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
+ cmdbuf.SetScissor(0, scissors);
});
return;
}
@@ -1388,7 +1442,17 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R
if (!state_tracker.TouchPrimitiveRestartEnable()) {
return;
}
- scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
+
+ bool enable = regs.primitive_restart.enabled != 0;
+ if (device.IsMoltenVK()) {
+ enable = true;
+ } else if (enable) {
+ const auto topology =
+ MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology);
+ enable = IsPrimitiveRestartSupported(device, topology);
+ }
+
+ scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable);
});
}
@@ -1727,7 +1791,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
auto& dirty{maxwell3d->dirty.flags};
- if (!dirty[Dirty::VertexInput]) {
+ const bool vertex_input_dirty = dirty[Dirty::VertexInput];
+ const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers];
+ if (!vertex_input_dirty && !vertex_buffers_dirty) {
return;
}
dirty[Dirty::VertexInput] = false;
@@ -1735,38 +1801,31 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings;
boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes;
- // There seems to be a bug on Nvidia's driver where updating only higher attributes ends up
- // generating dirty state. Track the highest dirty attribute and update all attributes until
- // that one.
- size_t highest_dirty_attr{};
- for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
- if (dirty[Dirty::VertexAttribute0 + index]) {
- highest_dirty_attr = index;
- }
- }
- for (size_t index = 0; index < highest_dirty_attr; ++index) {
+ const u32 max_attributes =
+ static_cast<u32>(std::min<size_t>(Maxwell::NumVertexAttributes,
+ device.GetMaxVertexInputAttributes()));
+ const u32 max_bindings =
+ static_cast<u32>(std::min<size_t>(Maxwell::NumVertexArrays,
+ device.GetMaxVertexInputBindings()));
+
+
+ for (u32 index = 0; index < max_attributes; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer};
- dirty[Dirty::VertexAttribute0 + index] = false;
- dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true;
- if (!attribute.constant) {
- attributes.push_back({
- .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
- .pNext = nullptr,
- .location = static_cast<u32>(index),
- .binding = binding,
- .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
- .offset = attribute.offset,
- });
- }
- }
- for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
- if (!dirty[Dirty::VertexBinding0 + index]) {
+ if (attribute.constant || binding >= max_bindings) {
continue;
}
- dirty[Dirty::VertexBinding0 + index] = false;
+ attributes.push_back({
+ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
+ .pNext = nullptr,
+ .location = index,
+ .binding = binding,
+ .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
+ .offset = attribute.offset,
+ });
+ }
- const u32 binding{static_cast<u32>(index)};
+ for (u32 binding = 0; binding < max_bindings; ++binding) {
const auto& input_binding{regs.vertex_streams[binding]};
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
bindings.push_back({
@@ -1778,6 +1837,14 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.divisor = is_instanced ? input_binding.frequency : 1,
});
}
+
+ for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
+ dirty[Dirty::VertexAttribute0 + index] = false;
+ }
+ for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
+ dirty[Dirty::VertexBinding0 + index] = false;
+ }
+
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes);
});
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index b689c6b660..841933d31d 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -122,6 +122,7 @@ public:
void FlushCommands() override;
void TickFrame() override;
bool AccelerateConditionalRendering() override;
+ bool HasDrawTransformFeedback() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 226619d8d6..fdaf9baacc 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -324,6 +324,8 @@ void Scheduler::EndRenderPass()
return;
}
+ query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
+
// Log render pass end
if (Settings::values.gpu_logging_enabled.GetValue() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 00a912f2cd..0709c3a370 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -63,6 +63,11 @@ public:
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
+ /// Returns true when a render pass is currently active in the scheduler state.
+ bool IsRenderPassActive() const {
+ return state.renderpass != VK_NULL_HANDLE;
+ }
+
/// Update the pipeline to the current execution context.
bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline);
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 79967d540a..3f4dd89c7e 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -87,6 +87,7 @@ Flags MakeInvalidationFlags() {
void SetupDirtyViewports(Tables& tables) {
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
+ FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports);
tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
tables[1][OFF(window_origin)] = Viewports;
}
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 74bae9e181..6b47ba4176 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 48aa5ec476..f099db74cb 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -176,7 +176,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.pViewFormats = view_formats.data(),
};
if (view_formats.size() > 1) {
- image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ image_ci.flags |=
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
+
+ const bool has_storage_compatible_view =
+ std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) {
+ return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
+ FormatType::Optimal);
+ });
+ if (has_storage_compatible_view) {
+ image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
+ }
+
if (device.IsKhrImageFormatListSupported()) {
image_ci.pNext = &image_format_list;
}
@@ -668,11 +679,16 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
}
void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
- bool emulate_a4b4g4r4) {
+ bool emulate_bgr565, bool emulate_a4b4g4r4) {
switch (format) {
case PixelFormat::A1B5G5R5_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
break;
+ case PixelFormat::B5G6R5_UNORM:
+ if (emulate_bgr565) {
+ std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
+ }
+ break;
case PixelFormat::A5B5G5R1_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
break;
@@ -2119,22 +2135,21 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
TryTransformSwizzleIfNeeded(format, swizzle,
- !device->IsExt4444FormatsSupported());
+ device->MustEmulateBGR565(),
+ !device->IsExt4444FormatsSupported());
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne());
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
- if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
- LOG_WARNING(Render_Vulkan,
- "Image view format {} has different usage flags than image format {}", format,
- image.info.format);
- }
+ const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
+ const VkImageUsageFlags image_usage = image.UsageFlags();
+ const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
const VkImageViewUsageCreateInfo image_view_usage{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.pNext = nullptr,
- .usage = ImageUsageFlags(format_info, format),
+ .usage = clamped_view_usage,
};
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -2300,23 +2315,18 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
const auto& device = runtime.device;
- // Check if custom border colors are supported
- const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported();
- const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported();
+ const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported();
+ const bool has_format_undefined =
+ has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported();
+ const bool has_custom_border_colors =
+ has_format_undefined && runtime.device.IsCustomBorderColorsSupported();
const auto color = tsc.BorderColor();
- // Determine border format based on available features:
- // - If customBorderColorWithoutFormat is available: use VK_FORMAT_UNDEFINED (most flexible)
- // - If only customBorderColors is available: use concrete format (R8G8B8A8_UNORM)
- // - If neither is available: use standard border colors (handled by ConvertBorderColor)
- const VkFormat border_format = has_format_undefined ? VK_FORMAT_UNDEFINED
- : VK_FORMAT_R8G8B8A8_UNORM;
-
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
.pNext = nullptr,
.customBorderColor = std::bit_cast<VkClearColorValue>(color),
- .format = border_format,
+ .format = VK_FORMAT_UNDEFINED,
};
const void* pnext = nullptr;
if (has_custom_border_colors) {