Compare commits

...

17 Commits

Author SHA1 Message Date
CamilleLaVey 699d35f8a6 Meow this shit 5.0 2026-01-28 01:39:17 -04:00
CamilleLaVey 93237dec5e Fix build 4.0 2026-01-28 01:28:17 -04:00
CamilleLaVey ec9e32fdcd Fix build 3.0 2026-01-28 01:15:00 -04:00
CamilleLaVey 769a4c043a fix build 2.0 2026-01-28 00:27:43 -04:00
CamilleLaVey 53dd313ee5 fix building 2026-01-28 00:19:09 -04:00
CamilleLaVey 8553e7e7de [vulkan] Increased logic handling of DynamicState and VertexInputDynamicState 2026-01-27 23:50:02 -04:00
CamilleLaVey e3adea7826 [vk, qcom] Added a handling for blitting image on Stock Drivers 2026-01-26 23:37:45 -04:00
CamilleLaVey c0d1438862 [vulkan, rasterizer] Executing UpdateDynamicStates() before BindPipeline function 2026-01-26 23:20:07 -04:00
CamilleLaVey b3d4dbca60 [vulkan] Re-odering pNext chain access for InlineUniformBlock + DescriptorBuffer 2026-01-26 21:48:37 -04:00
CamilleLaVey 477157d029 fix android crashing 2026-01-26 21:12:19 -04:00
lizzie 16bdb64ec9 fx 2026-01-26 23:42:51 +00:00
CamilleLaVey d565a250b6 fixing build 2026-01-26 20:51:42 +00:00
CamilleLaVey 354c5efde6 fixing headers 2026-01-26 20:51:42 +00:00
CamilleLaVey 6e17a47120 [vulkan] Fixing DynamicState wrong core feature callings 2026-01-26 20:51:42 +00:00
CamilleLaVey 90b312617b [vk] Added Descriptor Buffers 2026-01-26 20:51:42 +00:00
CamilleLaVey 86bb14bcd8 [vk] Added Inline Uniform Block 2026-01-26 20:51:42 +00:00
CamilleLaVey 63db10763d [vk] Added graphics pipeline library 2026-01-26 20:51:42 +00:00
17 changed files with 904 additions and 289 deletions
+6 -5
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -352,7 +352,8 @@ void BufferCache<P>::UpdateComputeBuffers() {
} }
template <class P> template <class P>
void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) { void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed, bool use_dynamic_vertex_input,
vk::CommandBufferPtr cmd) {
if (is_indexed) { if (is_indexed) {
BindHostIndexBuffer(); BindHostIndexBuffer();
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
@@ -363,7 +364,7 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
draw_state.vertex_buffer.count); draw_state.vertex_buffer.count);
} }
} }
BindHostVertexBuffers(); BindHostVertexBuffers(use_dynamic_vertex_input, cmd);
BindHostTransformFeedbackBuffers(); BindHostTransformFeedbackBuffers();
if (current_draw_indirect) { if (current_draw_indirect) {
BindHostDrawIndirectBuffers(); BindHostDrawIndirectBuffers();
@@ -764,7 +765,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
} }
template <class P> template <class P>
void BufferCache<P>::BindHostVertexBuffers() { void BufferCache<P>::BindHostVertexBuffers(bool use_dynamic_vertex_input, vk::CommandBufferPtr cmd) {
HostBindings<typename P::Buffer> host_bindings; HostBindings<typename P::Buffer> host_bindings;
bool any_valid{false}; bool any_valid{false};
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
@@ -800,7 +801,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
host_bindings.sizes.push_back(binding.size); host_bindings.sizes.push_back(binding.size);
host_bindings.strides.push_back(stride); host_bindings.strides.push_back(stride);
} }
runtime.BindVertexBuffers(host_bindings); runtime.BindVertexBuffers(host_bindings, use_dynamic_vertex_input, cmd);
} }
} }
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -38,6 +38,10 @@
namespace VideoCommon { namespace VideoCommon {
namespace vk {
using CommandBufferPtr = void*;
}
using BufferId = Common::SlotId; using BufferId = Common::SlotId;
using VideoCore::Surface::PixelFormat; using VideoCore::Surface::PixelFormat;
@@ -232,7 +236,8 @@ public:
void UpdateComputeBuffers(); void UpdateComputeBuffers();
void BindHostGeometryBuffers(bool is_indexed); void BindHostGeometryBuffers(bool is_indexed, bool use_dynamic_vertex_input = false,
vk::CommandBuffer* cmd = nullptr);
void BindHostStageBuffers(size_t stage); void BindHostStageBuffers(size_t stage);
@@ -358,7 +363,7 @@ private:
void BindHostIndexBuffer(); void BindHostIndexBuffer();
void BindHostVertexBuffers(); void BindHostVertexBuffers(bool use_dynamic_vertex_input = false, vk::CommandBuffer* cmd = nullptr);
void BindHostDrawIndirectBuffers(); void BindHostDrawIndirectBuffers();
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -242,7 +242,8 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool /*use_dynamic_vertex_input*/) {
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms? // TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
std::array<GLuint, 32> buffer_handles; std::array<GLuint, 32> buffer_handles;
std::array<GLsizei, 32> buffer_strides; std::array<GLsizei, 32> buffer_strides;
@@ -271,6 +272,13 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input,
VideoCommon::vk::CommandBufferPtr /*cmd*/) {
// Forward to the existing implementation; OpenGL doesn't use Vulkan command buffers.
BindVertexBuffers(bindings, use_dynamic_vertex_input);
}
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
u32 offset, u32 size) { u32 offset, u32 size) {
if (use_assembly_shaders) { if (use_assembly_shaders) {
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -101,7 +101,13 @@ public:
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input = false);
// Compatibility overload to allow code that provides an optional Vulkan command buffer
// pointer to compile for OpenGL. The pointer is ignored for OpenGL runtime.
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input, VideoCommon::vk::CommandBufferPtr cmd);
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -436,7 +436,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
bind_stage_info(4); bind_stage_info(4);
} }
buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed, false);
if (!IsBuilt()) { if (!IsBuilt()) {
WaitForBuild(); WaitForBuild();
+50 -6
View File
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <vector>
#include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_texture_cache.h"
@@ -761,10 +762,25 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key); const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
const VkPipelineLayout layout = *clear_color_pipeline_layout; const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer); scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) { const bool dyn_stencil_compare = device.SupportsDynamicStateStencilCompareMask();
const bool dyn_stencil_write = device.SupportsDynamicStateStencilWriteMask();
const bool dyn_stencil_ref = device.SupportsDynamicStateStencilReference();
scheduler.Record([pipeline, layout, clear_depth, dst_region, stencil_mask, stencil_ref,
stencil_compare_mask, dyn_stencil_compare, dyn_stencil_write,
dyn_stencil_ref](vk::CommandBuffer cmdbuf) {
constexpr std::array blend_constants{0.0f, 0.0f, 0.0f, 0.0f}; constexpr std::array blend_constants{0.0f, 0.0f, 0.0f, 0.0f};
cmdbuf.SetBlendConstants(blend_constants.data()); cmdbuf.SetBlendConstants(blend_constants.data());
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
if (dyn_stencil_compare) {
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, stencil_compare_mask);
}
if (dyn_stencil_write) {
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, stencil_mask);
}
if (dyn_stencil_ref) {
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, stencil_ref);
}
BindBlitState(cmdbuf, dst_region); BindBlitState(cmdbuf, dst_region);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth); cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth);
cmdbuf.Draw(3, 1, 0, 0); cmdbuf.Draw(3, 1, 0, 0);
@@ -1010,14 +1026,19 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
} }
clear_stencil_keys.push_back(key); clear_stencil_keys.push_back(key);
const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag); const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag);
// Allow using dynamic stencil masks if the device supports them.
const bool dyn_stencil_compare = device.SupportsDynamicStateStencilCompareMask();
const bool dyn_stencil_write = device.SupportsDynamicStateStencilWriteMask();
const bool dyn_stencil_ref = device.SupportsDynamicStateStencilReference();
const auto stencil = VkStencilOpState{ const auto stencil = VkStencilOpState{
.failOp = VK_STENCIL_OP_KEEP, .failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_REPLACE, .passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_KEEP, .depthFailOp = VK_STENCIL_OP_KEEP,
.compareOp = VK_COMPARE_OP_ALWAYS, .compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = key.stencil_compare_mask, .compareMask = dyn_stencil_compare ? 0u : key.stencil_compare_mask,
.writeMask = key.stencil_mask, .writeMask = dyn_stencil_write ? 0u : key.stencil_mask,
.reference = key.stencil_ref, .reference = dyn_stencil_ref ? 0u : key.stencil_ref,
}; };
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -1034,6 +1055,29 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.maxDepthBounds = 0.0f, .maxDepthBounds = 0.0f,
}; };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
// Build dynamic state list for this pipeline (base + optional stencil/linewidth)
std::vector<VkDynamicState> dyn_states(DYNAMIC_STATES.begin(), DYNAMIC_STATES.end());
if (device.SupportsDynamicStateLineWidth()) {
dyn_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
}
if (dyn_stencil_compare) {
dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
}
if (dyn_stencil_write) {
dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
}
if (dyn_stencil_ref) {
dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
}
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.dynamicStateCount = static_cast<u32>(dyn_states.size()),
.pDynamicStates = dyn_states.data(),
};
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@@ -1048,7 +1092,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &depth_stencil_ci, .pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pDynamicState = &dynamic_state_ci,
.layout = *clear_color_pipeline_layout, .layout = *clear_color_pipeline_layout,
.renderPass = key.renderpass, .renderPass = key.renderpass,
.subpass = 0, .subpass = 0,
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -171,6 +171,15 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
if (!extended_dynamic_state_3_enables) { if (!extended_dynamic_state_3_enables) {
dynamic_state.Refresh3(regs); dynamic_state.Refresh3(regs);
} }
if (features.has_depth_bounds) {
depth_bounds_min = 0;
depth_bounds_max = 0;
dynamic_state.depth_bounds_enable.Assign(0);
}
if (features.has_depth_bias) {
dynamic_state.depth_bias_enable.Assign(0);
}
if (xfb_enabled) { if (xfb_enabled) {
RefreshXfbState(xfb_state, regs); RefreshXfbState(xfb_state, regs);
} }
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -21,6 +21,15 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs; using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures { struct DynamicFeatures {
bool has_viewport;
bool has_scissor;
bool has_depth_bias;
bool has_blend_constants;
bool has_depth_bounds;
bool has_stencil_compare_mask;
bool has_stencil_write_mask;
bool has_stencil_reference;
bool has_line_width;
bool has_extended_dynamic_state; bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2; bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_logic_op; bool has_extended_dynamic_state_2_logic_op;
@@ -260,6 +269,9 @@ struct FixedPipelineState {
// When transform feedback is enabled, use the whole struct // When transform feedback is enabled, use the whole struct
return sizeof(*this); return sizeof(*this);
} }
if (extended_dynamic_state_3_blend) {
return offsetof(FixedPipelineState, attachments);
}
if (dynamic_vertex_input && extended_dynamic_state_3_blend) { if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
// Exclude dynamic state and attributes // Exclude dynamic state and attributes
return offsetof(FixedPipelineState, dynamic_state); return offsetof(FixedPipelineState, dynamic_state);
@@ -268,6 +280,9 @@ struct FixedPipelineState {
// Exclude dynamic state // Exclude dynamic state
return offsetof(FixedPipelineState, attributes); return offsetof(FixedPipelineState, attributes);
} }
if (extended_dynamic_state_3_enables) {
return offsetof(FixedPipelineState, dynamic_state);
}
if (extended_dynamic_state) { if (extended_dynamic_state) {
// Exclude dynamic state // Exclude dynamic state
return offsetof(FixedPipelineState, vertex_strides); return offsetof(FixedPipelineState, vertex_strides);
@@ -551,30 +551,46 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
} }
} }
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) { void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride,
bool use_dynamic_vertex_input, VideoCommon::vk::CommandBufferPtr cmd) {
if (index >= device.GetMaxVertexInputBindings()) { if (index >= device.GetMaxVertexInputBindings()) {
return; return;
} }
if (device.IsExtExtendedDynamicStateSupported()) { if (use_dynamic_vertex_input && device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { if (cmd) {
auto cmdbuf = static_cast<vk::CommandBuffer*>(cmd);
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
const VkDeviceSize vk_stride = stride; const VkDeviceSize vk_stride = stride;
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); cmdbuf->BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
}); } else {
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
const VkDeviceSize vk_stride = stride;
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
});
}
} else { } else {
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
ReserveNullBuffer(); ReserveNullBuffer();
buffer = *null_buffer; buffer = *null_buffer;
offset = 0; offset = 0;
} }
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.BindVertexBuffer(index, buffer, offset); auto cmdbuf = static_cast<vk::CommandBuffer*>(cmd);
}); cmdbuf->BindVertexBuffer(index, buffer, offset);
} else {
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffer(index, buffer, offset);
});
}
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input,
VideoCommon::vk::CommandBufferPtr cmd) {
boost::container::small_vector<VkBuffer, 32> buffer_handles; boost::container::small_vector<VkBuffer, 32> buffer_handles;
for (u32 index = 0; index < bindings.buffers.size(); ++index) { for (u32 index = 0; index < bindings.buffers.size(); ++index) {
auto handle = bindings.buffers[index]->Handle(); auto handle = bindings.buffers[index]->Handle();
@@ -595,21 +611,34 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
if (binding_count == 0) { if (binding_count == 0) {
return; return;
} }
if (device.IsExtExtendedDynamicStateSupported()) { if (use_dynamic_vertex_input && device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([bindings_ = std::move(bindings), if (cmd) {
buffer_handles_ = std::move(buffer_handles), auto cmdbuf = static_cast<vk::CommandBuffer*>(cmd);
binding_count](vk::CommandBuffer cmdbuf) { cmdbuf->BindVertexBuffers2EXT(bindings.min_index, binding_count, buffer_handles.data(),
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings.offsets.data(), bindings.sizes.data(),
bindings_.offsets.data(), bindings_.sizes.data(), bindings.strides.data());
bindings_.strides.data()); } else {
}); scheduler.Record([
bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles),
binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count,
buffer_handles_.data(), bindings_.offsets.data(),
bindings_.sizes.data(), bindings_.strides.data());
});
}
} else { } else {
scheduler.Record([bindings_ = std::move(bindings), if (cmd) {
buffer_handles_ = std::move(buffer_handles), auto cmdbuf = static_cast<vk::CommandBuffer*>(cmd);
binding_count](vk::CommandBuffer cmdbuf) { cmdbuf->BindVertexBuffers(bindings.min_index, binding_count, buffer_handles.data(),
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(), bindings.offsets.data());
bindings_.offsets.data()); } else {
}); scheduler.Record([
bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles),
binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count,
buffer_handles_.data(), bindings_.offsets.data());
});
}
} }
} }
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -123,9 +123,11 @@ public:
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride,
bool use_dynamic_vertex_input, VideoCommon::vk::CommandBufferPtr cmd = nullptr);
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input, VideoCommon::vk::CommandBufferPtr cmd = nullptr);
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -459,7 +459,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
} }
buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed, HasDynamicVertexInput());
guest_descriptor_queue.Acquire(); guest_descriptor_queue.Acquire();
@@ -519,6 +519,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
uses_render_area = render_area.uses_render_area, uses_render_area = render_area.uses_render_area,
render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { render_area_data = render_area.words](vk::CommandBuffer cmdbuf) {
if (bind_pipeline) { if (bind_pipeline) {
if (pre_bind_callback) pre_bind_callback(cmdbuf);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
} }
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
@@ -815,13 +816,35 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(), .pAttachments = cb_attachments.data(),
.blendConstants = {} .blendConstants = {}
}; };
static_vector<VkDynamicState, 34> dynamic_states{ static_vector<VkDynamicState, 34> dynamic_states;
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, if (device.SupportsDynamicStateViewport()) {
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, }
VK_DYNAMIC_STATE_LINE_WIDTH, if (device.SupportsDynamicStateScissor()) {
}; dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
}
if (device.SupportsDynamicStateDepthBias()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
}
if (device.SupportsDynamicStateBlendConstants()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
}
if (device.SupportsDynamicStateDepthBounds()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
}
if (device.SupportsDynamicStateStencilCompareMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
}
if (device.SupportsDynamicStateStencilWriteMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
}
if (device.SupportsDynamicStateStencilReference()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
}
if (device.SupportsDynamicStateLineWidth()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
}
if (key.state.extended_dynamic_state) { if (key.state.extended_dynamic_state) {
static constexpr std::array extended{ static constexpr std::array extended{
VK_DYNAMIC_STATE_CULL_MODE_EXT, VK_DYNAMIC_STATE_CULL_MODE_EXT,
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -21,6 +21,7 @@
#include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
#include <functional>
namespace VideoCore { namespace VideoCore {
class ShaderNotify; class ShaderNotify;
@@ -128,6 +129,9 @@ public:
gpu_memory = gpu_memory_; gpu_memory = gpu_memory_;
} }
void SetPreBindCallback(std::function<void(vk::CommandBuffer)> cb) { pre_bind_callback = std::move(cb); }
void ClearPreBindCallback() { pre_bind_callback = {};}
private: private:
template <typename Spec> template <typename Spec>
bool ConfigureImpl(bool is_indexed); bool ConfigureImpl(bool is_indexed);
@@ -135,6 +139,8 @@ private:
void ConfigureDraw(const RescalingPushConstant& rescaling, void ConfigureDraw(const RescalingPushConstant& rescaling,
const RenderAreaPushConstant& render_are); const RenderAreaPushConstant& render_are);
std::function<void(vk::CommandBuffer)> pre_bind_callback;
void MakePipeline(VkRenderPass render_pass); void MakePipeline(VkRenderPass render_pass);
void Validate(); void Validate();
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -418,12 +418,24 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
dynamic_features = {}; dynamic_features = {};
// User granularity enforced in vulkan_device.cpp switch statement: dynamic_features.has_viewport =
// Level 0: Core Dynamic States only device.SupportsDynamicStateViewport();
// Level 1: Core + EDS1 dynamic_features.has_scissor =
// Level 2: Core + EDS1 + EDS2 (accumulative) device.SupportsDynamicStateScissor();
// Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative) dynamic_features.has_depth_bias =
// Here we only verify if extensions were successfully loaded by the device device.SupportsDynamicStateDepthBias();
dynamic_features.has_blend_constants =
device.SupportsDynamicStateBlendConstants();
dynamic_features.has_depth_bounds =
device.SupportsDynamicStateDepthBounds();
dynamic_features.has_stencil_compare_mask =
device.SupportsDynamicStateStencilCompareMask();
dynamic_features.has_stencil_write_mask =
device.SupportsDynamicStateStencilWriteMask();
dynamic_features.has_stencil_reference =
device.SupportsDynamicStateStencilReference();
dynamic_features.has_line_width =
device.SupportsDynamicStateLineWidth();
dynamic_features.has_extended_dynamic_state = dynamic_features.has_extended_dynamic_state =
device.IsExtExtendedDynamicStateSupported(); device.IsExtExtendedDynamicStateSupported();
@@ -439,7 +451,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
dynamic_features.has_extended_dynamic_state_3_enables = dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported(); device.IsExtExtendedDynamicState3EnablesSupported();
// VIDS: Independent toggle (not affected by dyna_state levels)
dynamic_features.has_dynamic_vertex_input = dynamic_features.has_dynamic_vertex_input =
device.IsExtVertexInputDynamicStateSupported() && device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue(); Settings::values.vertex_input_dynamic_state.GetValue();
+524 -163
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -200,7 +200,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
// Log multi-draw support // Log multi-draw support
if (device.IsExtMultiDrawSupported()) { if (device.IsExtMultiDrawSupported()) {
LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw is enabled for optimized draw calls"); LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw is enabled for draw calls");
} }
} }
@@ -222,11 +222,18 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
// update engine as channel may be different. // update engine as channel may be different.
pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->SetEngine(maxwell3d, gpu_memory);
if (!pipeline->Configure(is_indexed))
return;
UpdateDynamicStates(); UpdateDynamicStates();
pipeline->SetPreBindCallback([this, is_indexed, pipeline](vk::CommandBuffer cmdbuf) {
RecordDynamicStates(cmdbuf);
buffer_cache.BindHostGeometryBuffers(is_indexed, pipeline->HasDynamicVertexInput(), &cmdbuf);
});
SCOPE_EXIT { pipeline->ClearPreBindCallback(); };
if (!pipeline->Configure(is_indexed))
return;
HandleTransformFeedback(); HandleTransformFeedback();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable); maxwell3d->regs.zpass_pixel_count_enable);
@@ -1046,6 +1053,199 @@ void RasterizerVulkan::UpdateDynamicStates() {
} }
} }
void RasterizerVulkan::RecordDynamicStates(vk::CommandBuffer cmdbuf) {
auto& regs = maxwell3d->regs;
// Viewports
if (state_tracker.TouchViewports()) {
if (!regs.viewport_scale_offset_enabled) {
float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y);
float width = (std::max)(1.0f, static_cast<float>(regs.surface_clip.width));
float height = (std::max)(1.0f, static_cast<float>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y += height;
height = -height;
}
VkViewport viewport{
.x = x,
.y = y,
.width = width,
.height = height,
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
if (!device.IsExtDepthRangeUnrestrictedSupported()) {
viewport.minDepth = std::clamp(viewport.minDepth, 0.0f, 1.0f);
viewport.maxDepth = std::clamp(viewport.maxDepth, 0.0f, 1.0f);
}
cmdbuf.SetViewport(0, viewport);
} else {
const bool is_rescaling{texture_cache.IsRescaling()};
const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
const std::array viewport_list{
GetViewportState(device, regs, 0, scale), GetViewportState(device, regs, 1, scale),
GetViewportState(device, regs, 2, scale), GetViewportState(device, regs, 3, scale),
GetViewportState(device, regs, 4, scale), GetViewportState(device, regs, 5, scale),
GetViewportState(device, regs, 6, scale), GetViewportState(device, regs, 7, scale),
GetViewportState(device, regs, 8, scale), GetViewportState(device, regs, 9, scale),
GetViewportState(device, regs, 10, scale), GetViewportState(device, regs, 11, scale),
GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale),
GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale),
};
const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
cmdbuf.SetViewport(0, viewports);
}
}
// Scissors
if (state_tracker.TouchScissors()) {
if (!regs.viewport_scale_offset_enabled) {
u32 x = regs.surface_clip.x;
u32 y = regs.surface_clip.y;
u32 width = (std::max)(1u, static_cast<u32>(regs.surface_clip.width));
u32 height = (std::max)(1u, static_cast<u32>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y = regs.surface_clip.height - (y + height);
}
VkRect2D scissor{};
scissor.offset.x = static_cast<int32_t>(x);
scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width;
scissor.extent.height = height;
cmdbuf.SetScissor(0, scissor);
} else {
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
up_scale = Settings::values.resolution_info.up_scale;
down_shift = Settings::values.resolution_info.down_shift;
}
const std::array scissor_list{
GetScissorState(regs, 0, up_scale, down_shift),
GetScissorState(regs, 1, up_scale, down_shift),
GetScissorState(regs, 2, up_scale, down_shift),
GetScissorState(regs, 3, up_scale, down_shift),
GetScissorState(regs, 4, up_scale, down_shift),
GetScissorState(regs, 5, up_scale, down_shift),
GetScissorState(regs, 6, up_scale, down_shift),
GetScissorState(regs, 7, up_scale, down_shift),
GetScissorState(regs, 8, up_scale, down_shift),
GetScissorState(regs, 9, up_scale, down_shift),
GetScissorState(regs, 10, up_scale, down_shift),
GetScissorState(regs, 11, up_scale, down_shift),
GetScissorState(regs, 12, up_scale, down_shift),
GetScissorState(regs, 13, up_scale, down_shift),
GetScissorState(regs, 14, up_scale, down_shift),
GetScissorState(regs, 15, up_scale, down_shift),
};
const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
cmdbuf.SetScissor(0, scissors);
}
}
// Depth bias
if (state_tracker.TouchDepthBias()) {
float units = regs.depth_bias / 2.0f;
const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT ||
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
if (is_d24 && !device.SupportsD24DepthBuffer()) {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
static constexpr const u64* start = NEEDS_D24;
static constexpr const u64* end = NEEDS_D24 + length;
const u64* it = std::find(start, end, program_id);
if (it != end) {
const double rescale_factor =
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
}
}
if (device.IsExtDepthBiasControlSupported()) {
static VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = VK_FALSE,
};
cmdbuf.SetDepthBias(units, regs.depth_bias_clamp, regs.slope_scale_depth_bias, &bias_info);
} else {
cmdbuf.SetDepthBias(units, regs.depth_bias_clamp, regs.slope_scale_depth_bias);
}
}
// Blend constants
if (state_tracker.TouchBlendConstants()) {
const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
regs.blend_color.a};
cmdbuf.SetBlendConstants(blend_color.data());
}
// Depth bounds
if (state_tracker.TouchDepthBounds()) {
cmdbuf.SetDepthBounds(regs.depth_bounds[0], regs.depth_bounds[1]);
}
// Stencil faces
if (state_tracker.TouchStencilProperties()) {
UpdateStencilOp(regs, &cmdbuf);
UpdateStencilTestEnable(regs, &cmdbuf);
}
// Line width
if (state_tracker.TouchLineWidth()) {
UpdateLineWidth(regs, &cmdbuf);
}
// Extended Dynamic State (a subset) - call existing helpers but target cmdbuf where appropriate
if (device.IsExtExtendedDynamicStateSupported()) {
UpdateCullMode(regs, &cmdbuf);
UpdateDepthCompareOp(regs, &cmdbuf);
UpdateFrontFace(regs, &cmdbuf);
UpdateStencilOp(regs, &cmdbuf);
if (state_tracker.TouchStateEnable()) {
UpdateDepthBoundsTestEnable(regs, &cmdbuf);
UpdateDepthTestEnable(regs, &cmdbuf);
UpdateDepthWriteEnable(regs, &cmdbuf);
UpdateStencilTestEnable(regs, &cmdbuf);
}
}
if (device.IsExtExtendedDynamicState2Supported()) {
UpdatePrimitiveRestartEnable(regs, &cmdbuf);
UpdateRasterizerDiscardEnable(regs, &cmdbuf);
UpdateDepthBiasEnable(regs, &cmdbuf);
}
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
UpdateLogicOp(regs, &cmdbuf);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
UpdateLogicOpEnable(regs, &cmdbuf);
UpdateDepthClampEnable(regs, &cmdbuf);
UpdateLineRasterizationMode(regs, &cmdbuf);
UpdateLineStippleEnable(regs, &cmdbuf);
UpdateConservativeRasterizationMode(regs, &cmdbuf);
UpdateAlphaToCoverageEnable(regs, &cmdbuf);
UpdateAlphaToOneEnable(regs, &cmdbuf);
}
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
UpdateBlending(regs, &cmdbuf);
}
if (device.IsExtVertexInputDynamicStateSupported()) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
UpdateVertexInput(regs, &cmdbuf);
}
}
}
void RasterizerVulkan::HandleTransformFeedback() { void RasterizerVulkan::HandleTransformFeedback() {
static std::once_flag warn_unsupported; static std::once_flag warn_unsupported;
@@ -1071,7 +1271,7 @@ void RasterizerVulkan::HandleTransformFeedback() {
} }
} }
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchViewports()) { if (!state_tracker.TouchViewports()) {
return; return;
} }
@@ -1092,9 +1292,11 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
.minDepth = 0.0f, .minDepth = 0.0f,
.maxDepth = 1.0f, .maxDepth = 1.0f,
}; };
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetViewport(0, viewport); cmd->SetViewport(0, viewport);
}); } else {
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
}
return; return;
} }
const bool is_rescaling{texture_cache.IsRescaling()}; const bool is_rescaling{texture_cache.IsRescaling()};
@@ -1109,14 +1311,20 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale), GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale),
GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale), GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale),
}; };
scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) { if (cmd) {
const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports); const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports); const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
cmdbuf.SetViewport(0, viewports); cmd->SetViewport(0, viewports);
}); } else {
scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) {
const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
cmdbuf.SetViewport(0, viewports);
});
}
} }
void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchScissors()) { if (!state_tracker.TouchScissors()) {
return; return;
} }
@@ -1133,9 +1341,11 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
scissor.offset.y = static_cast<int32_t>(y); scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width; scissor.extent.width = width;
scissor.extent.height = height; scissor.extent.height = height;
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetScissor(0, scissor); cmd->SetScissor(0, scissor);
}); } else {
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
}
return; return;
} }
u32 up_scale = 1; u32 up_scale = 1;
@@ -1162,14 +1372,20 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
GetScissorState(regs, 14, up_scale, down_shift), GetScissorState(regs, 14, up_scale, down_shift),
GetScissorState(regs, 15, up_scale, down_shift), GetScissorState(regs, 15, up_scale, down_shift),
}; };
scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) { if (cmd) {
const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports); const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors); const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
cmdbuf.SetScissor(0, scissors); cmd->SetScissor(0, scissors);
}); } else {
scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) {
const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
cmdbuf.SetScissor(0, scissors);
});
}
} }
void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthBias()) { if (!state_tracker.TouchDepthBias()) {
return; return;
} }
@@ -1196,8 +1412,7 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, if (cmd) {
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
if (device.IsExtDepthBiasControlSupported()) { if (device.IsExtDepthBiasControlSupported()) {
static VkDepthBiasRepresentationInfoEXT bias_info{ static VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT, .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
@@ -1207,32 +1422,57 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
.depthBiasExact = VK_FALSE, .depthBiasExact = VK_FALSE,
}; };
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info); cmd->SetDepthBias(units, regs.depth_bias_clamp, regs.slope_scale_depth_bias, &bias_info);
} else { } else {
cmdbuf.SetDepthBias(constant, clamp, factor); cmd->SetDepthBias(units, regs.depth_bias_clamp, regs.slope_scale_depth_bias);
} }
}); } else {
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
if (device.IsExtDepthBiasControlSupported()) {
static VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = VK_FALSE,
};
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
} else {
cmdbuf.SetDepthBias(constant, clamp, factor);
}
});
}
} }
void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchBlendConstants()) { if (!state_tracker.TouchBlendConstants()) {
return; return;
} }
const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
regs.blend_color.a}; regs.blend_color.a};
scheduler.Record( if (cmd) {
[blend_color](vk::CommandBuffer cmdbuf) { cmdbuf.SetBlendConstants(blend_color.data()); }); cmd->SetBlendConstants(blend_color.data());
} else {
scheduler.Record([blend_color](vk::CommandBuffer cmdbuf) { cmdbuf.SetBlendConstants(blend_color.data()); });
}
} }
void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthBounds()) { if (!state_tracker.TouchDepthBounds()) {
return; return;
} }
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]]( if (cmd) {
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); }); cmd->SetDepthBounds(regs.depth_bounds[0], regs.depth_bounds[1]);
} else {
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthBounds(min, max);
});
}
} }
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchStencilProperties()) { if (!state_tracker.TouchStencilProperties()) {
return; return;
} }
@@ -1256,17 +1496,25 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
return; return;
} }
} }
scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref, if (cmd) {
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { const bool set_back = regs.stencil_two_side_enable && regs.stencil_front_ref != regs.stencil_back_ref;
const bool set_back = two_sided && front_ref != back_ref; cmd->SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT : VK_STENCIL_FACE_FRONT_AND_BACK,
// Front face regs.stencil_front_ref);
cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
: VK_STENCIL_FACE_FRONT_AND_BACK,
front_ref);
if (set_back) { if (set_back) {
cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref); cmd->SetStencilReference(VK_STENCIL_FACE_BACK_BIT, regs.stencil_back_ref);
} }
}); } else {
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;
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);
}
});
}
}(); }();
} }
if (update_write_mask) { if (update_write_mask) {
@@ -1281,18 +1529,26 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
return; return;
} }
} }
scheduler.Record([front_write_mask = regs.stencil_front_mask, if (cmd) {
back_write_mask = regs.stencil_back_mask, const bool set_back = regs.stencil_two_side_enable && regs.stencil_front_mask != regs.stencil_back_mask;
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { cmd->SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT : VK_STENCIL_FACE_FRONT_AND_BACK,
const bool set_back = two_sided && front_write_mask != back_write_mask; regs.stencil_front_mask);
// Front face
cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
: VK_STENCIL_FACE_FRONT_AND_BACK,
front_write_mask);
if (set_back) { if (set_back) {
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask); cmd->SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, regs.stencil_back_mask);
} }
}); } else {
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;
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 (update_compare_masks) {
@@ -1307,43 +1563,59 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
return; return;
} }
} }
scheduler.Record([front_test_mask = regs.stencil_front_func_mask, if (cmd) {
back_test_mask = regs.stencil_back_func_mask, const bool set_back = regs.stencil_two_side_enable && regs.stencil_front_func_mask != regs.stencil_back_func_mask;
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) { cmd->SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT : VK_STENCIL_FACE_FRONT_AND_BACK,
const bool set_back = two_sided && front_test_mask != back_test_mask; regs.stencil_front_func_mask);
// Front face
cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
: VK_STENCIL_FACE_FRONT_AND_BACK,
front_test_mask);
if (set_back) { if (set_back) {
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask); cmd->SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, regs.stencil_back_func_mask);
} }
}); } else {
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;
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(); state_tracker.ClearStencilReset();
} }
void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchLineWidth()) { if (!state_tracker.TouchLineWidth()) {
return; return;
} }
const float width = const float width = regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased;
regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased; if (cmd) {
scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); cmd->SetLineWidth(width);
} else {
scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); });
}
} }
void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchCullMode()) { if (!state_tracker.TouchCullMode()) {
return; return;
} }
scheduler.Record([enabled = regs.gl_cull_test_enabled, if (cmd) {
cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) { cmd->SetCullModeEXT(regs.gl_cull_test_enabled ? MaxwellToVK::CullFace(regs.gl_cull_face)
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); : VK_CULL_MODE_NONE);
}); } else {
scheduler.Record([enabled = regs.gl_cull_test_enabled,
cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) {
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
});
}
} }
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthBoundsTestEnable()) { if (!state_tracker.TouchDepthBoundsTestEnable()) {
return; return;
} }
@@ -1352,48 +1624,60 @@ void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Re
LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported"); LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported");
enabled = false; enabled = false;
} }
scheduler.Record([enable = enabled](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetDepthBoundsTestEnableEXT(enable); cmd->SetDepthBoundsTestEnableEXT(enabled);
}); } else {
scheduler.Record([enable = enabled](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthBoundsTestEnableEXT(enable);
});
}
} }
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthTestEnable()) { if (!state_tracker.TouchDepthTestEnable()) {
return; return;
} }
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetDepthTestEnableEXT(enable); cmd->SetDepthTestEnableEXT(regs.depth_test_enable);
}); } else {
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthTestEnableEXT(enable); });
}
} }
void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthWriteEnable()) { if (!state_tracker.TouchDepthWriteEnable()) {
return; return;
} }
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetDepthWriteEnableEXT(enable); cmd->SetDepthWriteEnableEXT(regs.depth_write_enabled);
}); } else {
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthWriteEnableEXT(enable); });
}
} }
void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchPrimitiveRestartEnable()) { if (!state_tracker.TouchPrimitiveRestartEnable()) {
return; return;
} }
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable); cmd->SetPrimitiveRestartEnableEXT(regs.primitive_restart.enabled);
}); } else {
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetPrimitiveRestartEnableEXT(enable); });
}
} }
void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchRasterizerDiscardEnable()) { if (!state_tracker.TouchRasterizerDiscardEnable()) {
return; return;
} }
scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0); cmd->SetRasterizerDiscardEnableEXT(regs.rasterize_enable == 0);
}); } else {
scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0); });
}
} }
void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchConservativeRasterizationMode()) { if (!state_tracker.TouchConservativeRasterizationMode()) {
return; return;
} }
@@ -1401,15 +1685,20 @@ void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwe
if (!device.SupportsDynamicState3ConservativeRasterizationMode()) { if (!device.SupportsDynamicState3ConservativeRasterizationMode()) {
return; return;
} }
if (cmd) {
scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) { cmd->SetConservativeRasterizationModeEXT(
cmdbuf.SetConservativeRasterizationModeEXT( regs.conservative_raster_enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT); } else {
}); scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetConservativeRasterizationModeEXT(
enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);
});
}
} }
void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchLineStippleEnable()) { if (!state_tracker.TouchLineStippleEnable()) {
return; return;
} }
@@ -1417,13 +1706,16 @@ void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs&
if (!device.SupportsDynamicState3LineStippleEnable()) { if (!device.SupportsDynamicState3LineStippleEnable()) {
return; return;
} }
if (cmd) {
scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) { cmd->SetLineStippleEnableEXT(regs.line_stipple_enable);
cmdbuf.SetLineStippleEnableEXT(enable); } else {
}); scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetLineStippleEnableEXT(enable);
});
}
} }
void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!device.IsExtLineRasterizationSupported()) { if (!device.IsExtLineRasterizationSupported()) {
return; return;
} }
@@ -1455,12 +1747,16 @@ void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Re
}); });
} }
} }
scheduler.Record([mode](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetLineRasterizationModeEXT(mode); cmd->SetLineRasterizationModeEXT(mode);
}); } else {
scheduler.Record([mode](vk::CommandBuffer cmdbuf) {
cmdbuf.SetLineRasterizationModeEXT(mode);
});
}
} }
void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthBiasEnable()) { if (!state_tracker.TouchDepthBiasEnable()) {
return; return;
} }
@@ -1491,23 +1787,30 @@ void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& re
}; };
const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology); const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]]; const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
scheduler.Record( if (cmd) {
[enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); }); cmd->SetDepthBiasEnableEXT(enable != 0);
} else {
scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
}
} }
void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchLogicOpEnable()) { if (!state_tracker.TouchLogicOpEnable()) {
return; return;
} }
if (!device.SupportsDynamicState3LogicOpEnable()) { if (!device.SupportsDynamicState3LogicOpEnable()) {
return; return;
} }
scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetLogicOpEnableEXT(enable != 0); cmd->SetLogicOpEnableEXT(regs.logic_op.enable != 0);
}); } else {
scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetLogicOpEnableEXT(enable != 0);
});
}
} }
void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthClampEnable()) { if (!state_tracker.TouchDepthClampEnable()) {
return; return;
} }
@@ -1520,11 +1823,14 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip == regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ); Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
scheduler.Record( if (cmd) {
[is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); cmd->SetDepthClampEnableEXT(is_enabled);
} else {
scheduler.Record([is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
}
} }
void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchAlphaToCoverageEnable()) { if (!state_tracker.TouchAlphaToCoverageEnable()) {
return; return;
} }
@@ -1534,12 +1840,16 @@ void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Re
GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline();
const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() && const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() &&
regs.anti_alias_alpha_control.alpha_to_coverage != 0; regs.anti_alias_alpha_control.alpha_to_coverage != 0;
scheduler.Record([enable](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE); cmd->SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE);
}); } else {
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE);
});
}
} }
void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchAlphaToOneEnable()) { if (!state_tracker.TouchAlphaToOneEnable()) {
return; return;
} }
@@ -1554,21 +1864,29 @@ void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& r
GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline();
const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() && const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() &&
regs.anti_alias_alpha_control.alpha_to_one != 0; regs.anti_alias_alpha_control.alpha_to_one != 0;
scheduler.Record([enable](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE); cmd->SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE);
}); } else {
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE);
});
}
} }
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchDepthCompareOp()) { if (!state_tracker.TouchDepthCompareOp()) {
return; return;
} }
scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func)); cmd->SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(regs.depth_test_func));
}); } else {
scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
});
}
} }
void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchFrontFace()) { if (!state_tracker.TouchFrontFace()) {
return; return;
} }
@@ -1578,11 +1896,14 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
: VK_FRONT_FACE_CLOCKWISE; : VK_FRONT_FACE_CLOCKWISE;
} }
scheduler.Record( if (cmd) {
[front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); }); cmd->SetFrontFaceEXT(front_face);
} else {
scheduler.Record([front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
}
} }
void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchStencilOp()) { if (!state_tracker.TouchStencilOp()) {
return; return;
} }
@@ -1596,37 +1917,57 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail; const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail;
const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass; const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass;
const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func; const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func;
scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, if (cmd) {
back_compare](vk::CommandBuffer cmdbuf) { cmd->SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare)); MaxwellToVK::ComparisonOp(compare));
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail), cmd->SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
MaxwellToVK::StencilOp(back_zpass), MaxwellToVK::StencilOp(back_zpass),
MaxwellToVK::StencilOp(back_zfail), MaxwellToVK::StencilOp(back_zfail),
MaxwellToVK::ComparisonOp(back_compare)); MaxwellToVK::ComparisonOp(back_compare));
}); } else {
scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
back_compare](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare));
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
MaxwellToVK::StencilOp(back_zpass),
MaxwellToVK::StencilOp(back_zfail),
MaxwellToVK::ComparisonOp(back_compare));
});
}
} else { } else {
// Front face defines the stencil op of both faces // Front face defines the stencil op of both faces
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), cmd->SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare)); MaxwellToVK::ComparisonOp(compare));
}); } else {
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare));
});
}
} }
} }
void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchLogicOp()) { if (!state_tracker.TouchLogicOp()) {
return; return;
} }
const auto op_value = static_cast<u32>(regs.logic_op.op); 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) auto op = op_value >= 0x1500 && op_value < 0x1510 ? static_cast<VkLogicOp>(op_value - 0x1500)
: VK_LOGIC_OP_NO_OP; : VK_LOGIC_OP_NO_OP;
scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); }); if (cmd) {
cmd->SetLogicOpEXT(op);
} else {
scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
}
} }
void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchBlending()) { if (!state_tracker.TouchBlending()) {
return; return;
} }
@@ -1649,9 +1990,13 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
current |= VK_COLOR_COMPONENT_A_BIT; current |= VK_COLOR_COMPONENT_A_BIT;
} }
} }
scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetColorWriteMaskEXT(0, setup_masks); cmd->SetColorWriteMaskEXT(0, setup_masks);
}); } else {
scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorWriteMaskEXT(0, setup_masks);
});
}
} }
if (state_tracker.TouchBlendEnable()) { if (state_tracker.TouchBlendEnable()) {
@@ -1659,9 +2004,13 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
std::ranges::transform( std::ranges::transform(
regs.blend.enable, setup_enables.begin(), regs.blend.enable, setup_enables.begin(),
[&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; }); [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetColorBlendEnableEXT(0, setup_enables); cmd->SetColorBlendEnableEXT(0, setup_enables);
}); } else {
scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
});
}
} }
if (state_tracker.TouchBlendEquations()) { if (state_tracker.TouchBlendEquations()) {
@@ -1701,22 +2050,30 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends); cmd->SetColorBlendEquationEXT(0, setup_blends);
} else {
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
});
}
}
}
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchStencilTestEnable()) {
return;
}
if (cmd) {
cmd->SetStencilTestEnableEXT(regs.stencil_enable);
} else {
scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilTestEnableEXT(enable);
}); });
} }
} }
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd) {
if (!state_tracker.TouchStencilTestEnable()) {
return;
}
scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilTestEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
auto& dirty{maxwell3d->dirty.flags}; auto& dirty{maxwell3d->dirty.flags};
if (!dirty[Dirty::VertexInput]) { if (!dirty[Dirty::VertexInput]) {
return; return;
@@ -1769,9 +2126,13 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.divisor = is_instanced ? input_binding.frequency : 1, .divisor = is_instanced ? input_binding.frequency : 1,
}); });
} }
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { if (cmd) {
cmdbuf.SetVertexInputEXT(bindings, attributes); cmd->SetVertexInputEXT(bindings, attributes);
}); } else {
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes);
});
}
} }
void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
+31 -30
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -161,37 +161,38 @@ private:
void HandleTransformFeedback(); void HandleTransformFeedback();
void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmd = nullptr);
void RecordDynamicStates(vk::CommandBuffer cmdbuf);
Tegra::GPU& gpu; Tegra::GPU& gpu;
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;
+78 -29
View File
@@ -445,7 +445,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// GetSuitability has already configured the linked list of features for us. // GetSuitability has already configured the linked list of features for us.
// Reuse it here. // Reuse it here.
const void* first_next = &features2; void* first_next = &features2;
VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv{}; VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv{};
const bool use_diagnostics_nv = Settings::values.enable_nsight_aftermath && extensions.device_diagnostics_config; const bool use_diagnostics_nv = Settings::values.enable_nsight_aftermath && extensions.device_diagnostics_config;
@@ -462,16 +462,67 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
first_next = &diagnostics_nv; first_next = &diagnostics_nv;
} }
VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing{ // Query descriptor indexing features from the physical device first so we only
// request sub-features that are actually supported by the driver.
VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing_temp{};
descriptor_indexing_temp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
descriptor_indexing_temp.pNext = nullptr;
VkPhysicalDeviceFeatures2 desc_idx_features2{};
desc_idx_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
desc_idx_features2.pNext = &descriptor_indexing_temp;
physical.GetFeatures2(desc_idx_features2);
VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing_req{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
.pNext = use_diagnostics_nv ? static_cast<void*>(&diagnostics_nv) : static_cast<void*>(&features2), .pNext = use_diagnostics_nv ? static_cast<void*>(&diagnostics_nv) : static_cast<void*>(&features2),
.shaderSampledImageArrayNonUniformIndexing = VK_TRUE, .shaderSampledImageArrayNonUniformIndexing = descriptor_indexing_temp.shaderSampledImageArrayNonUniformIndexing,
.descriptorBindingPartiallyBound = VK_TRUE, .descriptorBindingPartiallyBound = descriptor_indexing_temp.descriptorBindingPartiallyBound,
.descriptorBindingVariableDescriptorCount = VK_TRUE, .descriptorBindingVariableDescriptorCount = descriptor_indexing_temp.descriptorBindingVariableDescriptorCount,
}; };
if (extensions.descriptor_indexing && Settings::values.descriptor_indexing.GetValue()) { if (extensions.descriptor_indexing && Settings::values.descriptor_indexing.GetValue()) {
first_next = &descriptor_indexing; first_next = &descriptor_indexing_req;
}
// VK_EXT_descriptor_buffer
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptor_buffer_temp{};
descriptor_buffer_temp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT;
descriptor_buffer_temp.pNext = nullptr;
VkPhysicalDeviceFeatures2 db_features2{};
db_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
db_features2.pNext = &descriptor_buffer_temp;
physical.GetFeatures2(db_features2);
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptor_buffer_features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
.pNext = first_next,
.descriptorBuffer = descriptor_buffer_temp.descriptorBuffer ? VK_TRUE : VK_FALSE,
.descriptorBufferCaptureReplay = descriptor_buffer_temp.descriptorBufferCaptureReplay ? VK_TRUE : VK_FALSE,
.descriptorBufferImageLayoutIgnored = descriptor_buffer_temp.descriptorBufferImageLayoutIgnored ? VK_TRUE : VK_FALSE,
.descriptorBufferPushDescriptors = descriptor_buffer_temp.descriptorBufferPushDescriptors ? VK_TRUE : VK_FALSE,
};
if (extensions.descriptor_buffer && descriptor_buffer_temp.descriptorBuffer) {
first_next = &descriptor_buffer_features;
}
// VK_EXT_inline_uniform_block
VkPhysicalDeviceInlineUniformBlockFeaturesEXT inline_uniform_block_temp{};
inline_uniform_block_temp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
inline_uniform_block_temp.pNext = nullptr;
VkPhysicalDeviceFeatures2 iub_features2{};
iub_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
iub_features2.pNext = &inline_uniform_block_temp;
physical.GetFeatures2(iub_features2);
VkPhysicalDeviceInlineUniformBlockFeaturesEXT inline_uniform_block_features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT,
.pNext = first_next,
.inlineUniformBlock = inline_uniform_block_temp.inlineUniformBlock ? VK_TRUE : VK_FALSE,
};
if (extensions.inline_uniform_block && inline_uniform_block_temp.inlineUniformBlock) {
first_next = &inline_uniform_block_features;
} }
is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT); is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT);
@@ -657,13 +708,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const auto dyna_state = Settings::values.dyna_state.GetValue(); const auto dyna_state = Settings::values.dyna_state.GetValue();
// Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp
// This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs:
// Level 0 = Core Dynamic States only (Vulkan 1.0)
// Level 1 = Core + VK_EXT_extended_dynamic_state
// Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2
// Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3
switch (dyna_state) { switch (dyna_state) {
case Settings::ExtendedDynamicState::Disabled: case Settings::ExtendedDynamicState::Disabled:
// Level 0: Disable all extended dynamic state extensions // Level 0: Disable all extended dynamic state extensions
@@ -698,8 +742,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
break; break;
} }
// VK_EXT_vertex_input_dynamic_state is independent from EDS // VK_EXT_vertex_input_dynamic_state is independent from ExtendedDynamicState level
// It can be enabled even without extended_dynamic_state
if (!Settings::values.vertex_input_dynamic_state.GetValue()) { if (!Settings::values.vertex_input_dynamic_state.GetValue()) {
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
@@ -1126,6 +1169,14 @@ bool Device::GetSuitability(bool requires_swapchain) {
SetNext(next, properties.multi_draw); SetNext(next, properties.multi_draw);
} }
// VK_EXT_descriptor_buffer properties
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_properties{};
if (extensions.descriptor_buffer) {
descriptor_buffer_properties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
SetNext(next, descriptor_buffer_properties);
}
// Perform the property fetch. // Perform the property fetch.
physical.GetProperties2(properties2); physical.GetProperties2(properties2);
@@ -1203,16 +1254,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false; features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false;
} }
// Return whether we were suitable.
return suitable; return suitable;
} }
void Device::RemoveUnsuitableExtensions() { void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color // VK_EXT_custom_border_color
// Enable extension if driver supports it, then check individual features
// - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
// - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED
// If only customBorderColors is available, we must provide a specific format
if (extensions.custom_border_color) { if (extensions.custom_border_color) {
// Verify that at least customBorderColors is available // Verify that at least customBorderColors is available
if (!features.custom_border_color.customBorderColors) { if (!features.custom_border_color.customBorderColors) {
@@ -1308,7 +1354,6 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_robustness2 // VK_EXT_robustness2
// Enable if at least one robustness2 feature is available
extensions.robustness_2 = features.robustness2.robustBufferAccess2 || extensions.robustness_2 = features.robustness2.robustBufferAccess2 ||
features.robustness2.robustImageAccess2 || features.robustness2.robustImageAccess2 ||
features.robustness2.nullDescriptor; features.robustness2.nullDescriptor;
@@ -1317,7 +1362,6 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// VK_EXT_image_robustness // VK_EXT_image_robustness
// Enable if robustImageAccess is available
extensions.image_robustness = features.image_robustness.robustImageAccess; extensions.image_robustness = features.image_robustness.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
@@ -1359,10 +1403,6 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
// VK_EXT_transform_feedback // VK_EXT_transform_feedback
// We only require the basic transformFeedback feature and at least
// one transform feedback buffer. We keep transformFeedbackQueries as it's used by
// the streaming byte count implementation. GeometryStreams and multiple streams
// are not strictly required since we currently support only stream 0.
extensions.transform_feedback = extensions.transform_feedback =
features.transform_feedback.transformFeedback && features.transform_feedback.transformFeedback &&
properties.transform_feedback.maxTransformFeedbackBuffers > 0 && properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
@@ -1475,17 +1515,26 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6,
VK_KHR_MAINTENANCE_6_EXTENSION_NAME); VK_KHR_MAINTENANCE_6_EXTENSION_NAME);
// VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features) // VK_KHR_maintenance7
extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
// VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features) // VK_KHR_maintenance8
extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
// VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features) // VK_KHR_maintenance9
extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
// VK_EXT_graphics_pipeline_library
extensions.graphics_pipeline_library = loaded_extensions.contains(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.graphics_pipeline_library, VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME);
if (extensions.graphics_pipeline_library) {
LOG_INFO(Render_Vulkan, "Driver supports VK_EXT_graphics_pipeline_library; enabling use");
} else {
LOG_INFO(Render_Vulkan, "VK_EXT_graphics_pipeline_library not available");
}
} }
void Device::SetupFamilies(VkSurfaceKHR surface) { void Device::SetupFamilies(VkSurfaceKHR surface) {
+46 -1
View File
@@ -105,7 +105,10 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \
EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \
EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) \
EXTENSION(EXT, GRAPHICS_PIPELINE_LIBRARY, graphics_pipeline_library) \
EXTENSION(EXT, DESCRIPTOR_BUFFER, descriptor_buffer) \
EXTENSION(EXT, INLINE_UNIFORM_BLOCK, inline_uniform_block) \
// Define extensions which must be supported. // Define extensions which must be supported.
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
@@ -293,6 +296,11 @@ public:
return properties.properties.driverVersion; return properties.properties.driverVersion;
} }
/// Returns true if the device and driver have VK_EXT_graphics_pipeline_library enabled.
bool HasGraphicsPipelineLibrary() const {
return extensions.graphics_pipeline_library;
}
/// Returns the device name. /// Returns the device name.
std::string_view GetModelName() const { std::string_view GetModelName() const {
return properties.properties.deviceName; return properties.properties.deviceName;
@@ -696,6 +704,43 @@ public:
return dynamic_state3_alpha_to_one; return dynamic_state3_alpha_to_one;
} }
// Core dynamic state support checks
bool SupportsDynamicStateViewport() const {
return dld.vkCmdSetViewport != nullptr;
}
bool SupportsDynamicStateScissor() const {
return dld.vkCmdSetScissor != nullptr;
}
bool SupportsDynamicStateDepthBias() const {
return dld.vkCmdSetDepthBias != nullptr || dld.vkCmdSetDepthBias2EXT != nullptr;
}
bool SupportsDynamicStateBlendConstants() const {
return dld.vkCmdSetBlendConstants != nullptr;
}
bool SupportsDynamicStateDepthBounds() const {
return dld.vkCmdSetDepthBounds != nullptr && IsDepthBoundsSupported();
}
bool SupportsDynamicStateStencilCompareMask() const {
return dld.vkCmdSetStencilCompareMask != nullptr;
}
bool SupportsDynamicStateStencilWriteMask() const {
return dld.vkCmdSetStencilWriteMask != nullptr;
}
bool SupportsDynamicStateStencilReference() const {
return dld.vkCmdSetStencilReference != nullptr;
}
bool SupportsDynamicStateLineWidth() const {
return dld.vkCmdSetLineWidth != nullptr;
}
/// Returns true if the device supports VK_EXT_vertex_input_dynamic_state. /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
bool IsExtVertexInputDynamicStateSupported() const { bool IsExtVertexInputDynamicStateSupported() const {
return extensions.vertex_input_dynamic_state; return extensions.vertex_input_dynamic_state;