[vulkan] Increased logic handling of DynamicState and VertexInputDynamicState

This commit is contained in:
CamilleLaVey 2026-01-27 23:50:02 -04:00
parent e3adea7826
commit 8553e7e7de
12 changed files with 599 additions and 220 deletions

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,7 @@ 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) {
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 +363,7 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
draw_state.vertex_buffer.count); draw_state.vertex_buffer.count);
} }
} }
BindHostVertexBuffers(); BindHostVertexBuffers(use_dynamic_vertex_input);
BindHostTransformFeedbackBuffers(); BindHostTransformFeedbackBuffers();
if (current_draw_indirect) { if (current_draw_indirect) {
BindHostDrawIndirectBuffers(); BindHostDrawIndirectBuffers();
@ -764,7 +764,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
} }
template <class P> template <class P>
void BufferCache<P>::BindHostVertexBuffers() { void BufferCache<P>::BindHostVertexBuffers(bool use_dynamic_vertex_input) {
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 +800,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);
} }
} }

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
@ -232,7 +232,7 @@ public:
void UpdateComputeBuffers(); void UpdateComputeBuffers();
void BindHostGeometryBuffers(bool is_indexed); void BindHostGeometryBuffers(bool is_indexed, bool use_dynamic_vertex_input = false);
void BindHostStageBuffers(size_t stage); void BindHostStageBuffers(size_t stage);
@ -358,7 +358,7 @@ private:
void BindHostIndexBuffer(); void BindHostIndexBuffer();
void BindHostVertexBuffers(); void BindHostVertexBuffers(bool use_dynamic_vertex_input = false);
void BindHostDrawIndirectBuffers(); void BindHostDrawIndirectBuffers();

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 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;

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 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -101,7 +101,8 @@ 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);
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);

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 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();

View file

@ -551,11 +551,12 @@ 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) {
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) { scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
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;
@ -574,7 +575,8 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input) {
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,7 +597,7 @@ 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), scheduler.Record([bindings_ = std::move(bindings),
buffer_handles_ = std::move(buffer_handles), buffer_handles_ = std::move(buffer_handles),
binding_count](vk::CommandBuffer cmdbuf) { binding_count](vk::CommandBuffer cmdbuf) {

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
@ -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);
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings,
bool use_dynamic_vertex_input);
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);

View file

@ -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,

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 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();

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
@ -225,6 +225,9 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates(); UpdateDynamicStates();
pipeline->SetPreBindCallback([this](vk::CommandBuffer cmdbuf) { RecordDynamicStates(cmdbuf); });
SCOPE_EXIT { pipeline->ClearPreBindCallback(); };
if (!pipeline->Configure(is_indexed)) if (!pipeline->Configure(is_indexed))
return; return;
@ -1047,6 +1050,219 @@ 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()) {
const VkStencilOpState front_state{
.failOp = static_cast<VkStencilOp>(regs.stencil_front_fail),
.passOp = static_cast<VkStencilOp>(regs.stencil_front_pass),
.depthFailOp = static_cast<VkStencilOp>(regs.stencil_front_depth_fail),
.compareOp = static_cast<VkCompareOp>(regs.stencil_front_compare),
.compareMask = regs.stencil_front_mask,
.writeMask = regs.stencil_front_write_mask,
.reference = regs.stencil_front_reference,
};
const VkStencilOpState back_state{
.failOp = static_cast<VkStencilOp>(regs.stencil_back_fail),
.passOp = static_cast<VkStencilOp>(regs.stencil_back_pass),
.depthFailOp = static_cast<VkStencilOp>(regs.stencil_back_depth_fail),
.compareOp = static_cast<VkCompareOp>(regs.stencil_back_compare),
.compareMask = regs.stencil_back_mask,
.writeMask = regs.stencil_back_write_mask,
.reference = regs.stencil_back_reference,
};
cmdbuf.SetStencilOpEXT(front_state, back_state);
if (state_tracker.TouchStencilReference()) {
cmdbuf.SetStencilReference(front_state.reference, back_state.reference);
}
}
// Line width
if (state_tracker.TouchLineWidth()) {
cmdbuf.SetLineWidth(regs.line_width);
}
// 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;
@ -1072,7 +1288,7 @@ void RasterizerVulkan::HandleTransformFeedback() {
} }
} }
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf) {
if (!state_tracker.TouchViewports()) { if (!state_tracker.TouchViewports()) {
return; return;
} }
@ -1093,9 +1309,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 (cmdbuf) {
cmdbuf.SetViewport(0, viewport); cmdbuf->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()};
@ -1110,14 +1328,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 (cmdbuf) {
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); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchScissors()) { if (!state_tracker.TouchScissors()) {
return; return;
} }
@ -1134,9 +1358,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 (cmdbuf) {
cmdbuf.SetScissor(0, scissor); cmdbuf->SetScissor(0, scissor);
}); } else {
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
}
return; return;
} }
u32 up_scale = 1; u32 up_scale = 1;
@ -1163,14 +1389,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 (cmdbuf) {
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); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthBias()) { if (!state_tracker.TouchDepthBias()) {
return; return;
} }
@ -1197,8 +1429,7 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, if (cmdbuf) {
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,
@ -1208,32 +1439,57 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
.depthBiasExact = VK_FALSE, .depthBiasExact = VK_FALSE,
}; };
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info); cmdbuf->SetDepthBias(units, regs.depth_bias_clamp, regs.slope_scale_depth_bias, &bias_info);
} else { } else {
cmdbuf.SetDepthBias(constant, clamp, factor); cmdbuf->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* cmdbuf) {
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 (cmdbuf) {
[blend_color](vk::CommandBuffer cmdbuf) { cmdbuf.SetBlendConstants(blend_color.data()); }); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthBounds()) { if (!state_tracker.TouchDepthBounds()) {
return; return;
} }
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]]( if (cmdbuf) {
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); }); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchStencilProperties()) { if (!state_tracker.TouchStencilProperties()) {
return; return;
} }
@ -1257,17 +1513,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 (cmdbuf) {
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; cmdbuf->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); cmdbuf->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) {
@ -1282,18 +1546,26 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
return; return;
} }
} }
scheduler.Record([front_write_mask = regs.stencil_front_mask, if (cmdbuf) {
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) { cmdbuf->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); cmdbuf->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) {
@ -1308,40 +1580,56 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
return; return;
} }
} }
scheduler.Record([front_test_mask = regs.stencil_front_func_mask, if (cmdbuf) {
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) { cmdbuf->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); cmdbuf->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* cmdbuf) {
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 (cmdbuf) {
scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchCullMode()) { if (!state_tracker.TouchCullMode()) {
return; return;
} }
scheduler.Record([enabled = regs.gl_cull_test_enabled, if (cmdbuf) {
cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) { cmdbuf->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) {
@ -1358,43 +1646,51 @@ void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Re
}); });
} }
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf) {
if (!state_tracker.TouchDepthTestEnable()) { if (!state_tracker.TouchDepthTestEnable()) {
return; return;
} }
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetDepthTestEnableEXT(enable); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthWriteEnable()) { if (!state_tracker.TouchDepthWriteEnable()) {
return; return;
} }
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetDepthWriteEnableEXT(enable); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchPrimitiveRestartEnable()) { if (!state_tracker.TouchPrimitiveRestartEnable()) {
return; return;
} }
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchRasterizerDiscardEnable()) { if (!state_tracker.TouchRasterizerDiscardEnable()) {
return; return;
} }
scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchConservativeRasterizationMode()) { if (!state_tracker.TouchConservativeRasterizationMode()) {
return; return;
} }
@ -1402,15 +1698,20 @@ void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwe
if (!device.SupportsDynamicState3ConservativeRasterizationMode()) { if (!device.SupportsDynamicState3ConservativeRasterizationMode()) {
return; return;
} }
if (cmdbuf) {
scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) { cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchLineStippleEnable()) { if (!state_tracker.TouchLineStippleEnable()) {
return; return;
} }
@ -1418,13 +1719,16 @@ void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs&
if (!device.SupportsDynamicState3LineStippleEnable()) { if (!device.SupportsDynamicState3LineStippleEnable()) {
return; return;
} }
if (cmdbuf) {
scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) { cmdbuf->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* cmdbuf) {
if (!device.IsExtLineRasterizationSupported()) { if (!device.IsExtLineRasterizationSupported()) {
return; return;
} }
@ -1456,12 +1760,16 @@ void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Re
}); });
} }
} }
scheduler.Record([mode](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetLineRasterizationModeEXT(mode); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthBiasEnable()) { if (!state_tracker.TouchDepthBiasEnable()) {
return; return;
} }
@ -1492,23 +1800,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 (cmdbuf) {
[enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); }); cmdbuf->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* cmdbuf) {
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 (cmdbuf) {
cmdbuf.SetLogicOpEnableEXT(enable != 0); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthClampEnable()) { if (!state_tracker.TouchDepthClampEnable()) {
return; return;
} }
@ -1521,11 +1836,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 (cmdbuf) {
[is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchAlphaToCoverageEnable()) { if (!state_tracker.TouchAlphaToCoverageEnable()) {
return; return;
} }
@ -1535,12 +1853,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 (cmdbuf) {
cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchAlphaToOneEnable()) { if (!state_tracker.TouchAlphaToOneEnable()) {
return; return;
} }
@ -1555,21 +1877,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 (cmdbuf) {
cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchDepthCompareOp()) { if (!state_tracker.TouchDepthCompareOp()) {
return; return;
} }
scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func)); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchFrontFace()) { if (!state_tracker.TouchFrontFace()) {
return; return;
} }
@ -1579,11 +1909,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 (cmdbuf) {
[front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); }); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchStencilOp()) { if (!state_tracker.TouchStencilOp()) {
return; return;
} }
@ -1597,37 +1930,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 (cmdbuf) {
back_compare](vk::CommandBuffer cmdbuf) { cmdbuf->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), cmdbuf->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 (cmdbuf) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), cmdbuf->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* cmdbuf) {
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 (cmdbuf) {
cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchBlending()) { if (!state_tracker.TouchBlending()) {
return; return;
} }
@ -1650,9 +2003,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 (cmdbuf) {
cmdbuf.SetColorWriteMaskEXT(0, setup_masks); cmdbuf->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()) {
@ -1660,9 +2017,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 (cmdbuf) {
cmdbuf.SetColorBlendEnableEXT(0, setup_enables); cmdbuf->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()) {
@ -1702,22 +2063,30 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) { if (cmdbuf) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends); cmdbuf->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* cmdbuf) {
if (!state_tracker.TouchStencilTestEnable()) {
return;
}
if (cmdbuf) {
cmdbuf->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* cmdbuf) {
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;
@ -1770,9 +2139,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 (cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes); cmdbuf->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) {

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* cmdbuf = nullptr);
void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, vk::CommandBuffer* cmdbuf = nullptr);
void RecordDynamicStates(vk::CommandBuffer cmdbuf);
Tegra::GPU& gpu; Tegra::GPU& gpu;
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;

View file

@ -663,11 +663,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
"allowing Eden to use {} (75%) to avoid heap exhaustion", "allowing Eden to use {} (75%) to avoid heap exhaustion",
sampler_limit, reserved, sampler_heap_budget); sampler_limit, reserved, sampler_heap_budget);
} }
// Qualcomm proprietary drivers have issues with MSAA->MSAA image blits.
LOG_WARNING(Render_Vulkan,
"Qualcomm drivers do not support MSAA->MSAA image blits. "
"MSAA scaling will use 3D helpers. MSAA resolves work normally.");
cant_blit_msaa = true;
} }
if (extensions.sampler_filter_minmax && is_amd) { if (extensions.sampler_filter_minmax && is_amd) {
@ -1259,7 +1254,6 @@ 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;
} }
@ -1360,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;
@ -1369,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);
@ -1523,15 +1515,15 @@ 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);