renderer_vulkan: Only update dynamic state when changed. (#2751)
Some checks are pending
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / reuse (push) Waiting to run
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / linux-sdl-gcc (push) Blocked by required conditions
Build and Release / linux-qt-gcc (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions

This commit is contained in:
squidbus 2025-04-08 07:22:16 -07:00 committed by GitHub
parent 08731303d8
commit 03b1fef331
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 414 additions and 86 deletions

View file

@ -946,19 +946,19 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) {
mapped_ranges -= boost::icl::interval<VAddr>::right_open(addr, addr + size);
}
void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const {
UpdateViewportScissorState();
UpdateDepthStencilState();
const auto& regs = liverpool->regs;
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.setBlendConstants(&regs.blend_constants.red);
if (instance.IsDynamicColorWriteMaskSupported()) {
cmdbuf.setColorWriteMaskEXT(0, pipeline.GetWriteMasks());
}
auto& dynamic_state = scheduler.GetDynamicState();
dynamic_state.SetBlendConstants(&liverpool->regs.blend_constants.red);
dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks());
// Commit new dynamic state to the command buffer.
dynamic_state.Commit(instance, scheduler.CommandBuffer());
}
void Rasterizer::UpdateViewportScissorState() {
void Rasterizer::UpdateViewportScissorState() const {
const auto& regs = liverpool->regs;
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
@ -1071,92 +1071,65 @@ void Rasterizer::UpdateViewportScissorState() {
scissors.push_back(empty_scissor);
}
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.setViewportWithCountEXT(viewports);
cmdbuf.setScissorWithCountEXT(scissors);
auto& dynamic_state = scheduler.GetDynamicState();
dynamic_state.SetViewports(viewports);
dynamic_state.SetScissors(scissors);
}
void Rasterizer::UpdateDepthStencilState() {
auto& regs = liverpool->regs;
const auto cmdbuf = scheduler.CommandBuffer();
void Rasterizer::UpdateDepthStencilState() const {
const auto& regs = liverpool->regs;
auto& dynamic_state = scheduler.GetDynamicState();
bool depth_test = regs.depth_control.depth_enable && regs.depth_buffer.DepthValid();
cmdbuf.setDepthTestEnableEXT(depth_test);
cmdbuf.setDepthWriteEnableEXT(regs.depth_control.depth_write_enable &&
!regs.depth_render_control.depth_clear_enable);
if (depth_test) {
cmdbuf.setDepthCompareOpEXT(LiverpoolToVK::CompareOp(regs.depth_control.depth_func));
const auto depth_test_enabled =
regs.depth_control.depth_enable && regs.depth_buffer.DepthValid();
dynamic_state.SetDepthTestEnabled(depth_test_enabled);
if (depth_test_enabled) {
dynamic_state.SetDepthWriteEnabled(regs.depth_control.depth_write_enable &&
!regs.depth_render_control.depth_clear_enable);
dynamic_state.SetDepthCompareOp(LiverpoolToVK::CompareOp(regs.depth_control.depth_func));
}
if (instance.IsDepthBoundsSupported()) {
cmdbuf.setDepthBoundsTestEnableEXT(regs.depth_control.depth_bounds_enable);
if (regs.depth_control.depth_bounds_enable) {
cmdbuf.setDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max);
}
const auto depth_bounds_test_enabled = regs.depth_control.depth_bounds_enable;
dynamic_state.SetDepthBoundsTestEnabled(depth_bounds_test_enabled);
if (depth_bounds_test_enabled) {
dynamic_state.SetDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max);
}
cmdbuf.setDepthBiasEnableEXT(regs.polygon_control.NeedsBias());
if (regs.polygon_control.enable_polygon_offset_front) {
cmdbuf.setDepthBias(regs.poly_offset.front_offset, regs.poly_offset.depth_bias,
regs.poly_offset.front_scale / 16.f);
} else if (regs.polygon_control.enable_polygon_offset_back) {
cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias,
regs.poly_offset.back_scale / 16.f);
const auto depth_bias_enabled = regs.polygon_control.NeedsBias();
if (depth_bias_enabled) {
dynamic_state.SetDepthBias(
regs.polygon_control.enable_polygon_offset_front ? regs.poly_offset.front_offset
: regs.poly_offset.back_offset,
regs.poly_offset.depth_bias,
(regs.polygon_control.enable_polygon_offset_front ? regs.poly_offset.front_scale
: regs.poly_offset.back_scale) /
16.f);
}
cmdbuf.setStencilTestEnableEXT(regs.depth_control.stencil_enable &&
regs.depth_buffer.StencilValid());
if (regs.depth_control.stencil_enable) {
const auto front_fail_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front);
const auto front_pass_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front);
const auto front_depth_fail_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front);
const auto front_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func);
if (regs.depth_control.backface_enable) {
const auto back_fail_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back);
const auto back_pass_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back);
const auto back_depth_fail_op =
LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back);
const auto back_compare_op =
LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func);
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, front_fail_op, front_pass_op,
front_depth_fail_op, front_compare_op);
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, back_fail_op, back_pass_op,
back_depth_fail_op, back_compare_op);
} else {
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, front_fail_op,
front_pass_op, front_depth_fail_op, front_compare_op);
}
const auto stencil_test_enabled =
regs.depth_control.stencil_enable && regs.depth_buffer.StencilValid();
dynamic_state.SetStencilTestEnabled(stencil_test_enabled);
if (stencil_test_enabled) {
const StencilOps front_ops{
.fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front),
.pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front),
.depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front),
.compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func),
};
const StencilOps back_ops = regs.depth_control.backface_enable ? StencilOps{
.fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back),
.pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back),
.depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back),
.compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func),
} : front_ops;
dynamic_state.SetStencilOps(front_ops, back_ops);
const auto front = regs.stencil_ref_front;
const auto back = regs.stencil_ref_back;
if (front.stencil_test_val == back.stencil_test_val) {
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
front.stencil_test_val);
} else {
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, front.stencil_test_val);
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, back.stencil_test_val);
}
if (front.stencil_write_mask == back.stencil_write_mask) {
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
front.stencil_write_mask);
} else {
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, front.stencil_write_mask);
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, back.stencil_write_mask);
}
if (front.stencil_mask == back.stencil_mask) {
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
front.stencil_mask);
} else {
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, front.stencil_mask);
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack, back.stencil_mask);
}
const auto back =
regs.depth_control.backface_enable ? regs.stencil_ref_back : regs.stencil_ref_front;
dynamic_state.SetStencilReferences(front.stencil_test_val, back.stencil_test_val);
dynamic_state.SetStencilWriteMasks(front.stencil_write_mask, back.stencil_write_mask);
dynamic_state.SetStencilCompareMasks(front.stencil_mask, back.stencil_mask);
}
}

View file

@ -75,9 +75,9 @@ private:
void DepthStencilCopy(bool is_depth, bool is_stencil);
void EliminateFastClear();
void UpdateDynamicState(const GraphicsPipeline& pipeline);
void UpdateViewportScissorState();
void UpdateDepthStencilState();
void UpdateDynamicState(const GraphicsPipeline& pipeline) const;
void UpdateViewportScissorState() const;
void UpdateDepthStencilState() const;
bool FilterDraw();

View file

@ -97,6 +97,9 @@ void Scheduler::AllocateWorkerCommandBuffers() {
ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}",
vk::to_string(begin_result));
// Invalidate dynamic state so it gets applied to the new command buffer.
dynamic_state.Invalidate();
#if TRACY_GPU_ENABLED
auto* profiler_ctx = instance.GetProfilerContext();
if (profiler_ctx) {
@ -164,4 +167,137 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
}
}
void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) {
if (dirty_state.viewports) {
dirty_state.viewports = false;
cmdbuf.setViewportWithCountEXT(viewports);
}
if (dirty_state.scissors) {
dirty_state.scissors = false;
cmdbuf.setScissorWithCountEXT(scissors);
}
if (dirty_state.depth_test_enabled) {
dirty_state.depth_test_enabled = false;
cmdbuf.setDepthTestEnableEXT(depth_test_enabled);
}
if (dirty_state.depth_write_enabled) {
dirty_state.depth_write_enabled = false;
// Note that this must be set in a command buffer even if depth test is disabled.
cmdbuf.setDepthWriteEnableEXT(depth_write_enabled);
}
if (depth_test_enabled && dirty_state.depth_compare_op) {
dirty_state.depth_compare_op = false;
cmdbuf.setDepthCompareOpEXT(depth_compare_op);
}
if (dirty_state.depth_bounds_test_enabled) {
dirty_state.depth_bounds_test_enabled = false;
if (instance.IsDepthBoundsSupported()) {
cmdbuf.setDepthBoundsTestEnableEXT(depth_bounds_test_enabled);
}
}
if (depth_bounds_test_enabled && dirty_state.depth_bounds) {
dirty_state.depth_bounds = false;
if (instance.IsDepthBoundsSupported()) {
cmdbuf.setDepthBounds(depth_bounds_min, depth_bounds_max);
}
}
if (dirty_state.depth_bias_enabled) {
dirty_state.depth_bias_enabled = false;
cmdbuf.setDepthBiasEnableEXT(depth_bias_enabled);
}
if (depth_bias_enabled && dirty_state.depth_bias) {
dirty_state.depth_bias = false;
cmdbuf.setDepthBias(depth_bias_constant, depth_bias_clamp, depth_bias_slope);
}
if (dirty_state.stencil_test_enabled) {
dirty_state.stencil_test_enabled = false;
cmdbuf.setStencilTestEnableEXT(stencil_test_enabled);
}
if (stencil_test_enabled) {
if (dirty_state.stencil_front_ops && dirty_state.stencil_back_ops &&
stencil_front_ops == stencil_back_ops) {
dirty_state.stencil_front_ops = false;
dirty_state.stencil_back_ops = false;
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack,
stencil_front_ops.fail_op, stencil_front_ops.pass_op,
stencil_front_ops.depth_fail_op, stencil_front_ops.compare_op);
} else {
if (dirty_state.stencil_front_ops) {
dirty_state.stencil_front_ops = false;
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op,
stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op,
stencil_front_ops.compare_op);
}
if (dirty_state.stencil_back_ops) {
dirty_state.stencil_back_ops = false;
cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op,
stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op,
stencil_back_ops.compare_op);
}
}
if (dirty_state.stencil_front_reference && dirty_state.stencil_back_reference &&
stencil_front_reference == stencil_back_reference) {
dirty_state.stencil_front_reference = false;
dirty_state.stencil_back_reference = false;
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
stencil_front_reference);
} else {
if (dirty_state.stencil_front_reference) {
dirty_state.stencil_front_reference = false;
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront,
stencil_front_reference);
}
if (dirty_state.stencil_back_reference) {
dirty_state.stencil_back_reference = false;
cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, stencil_back_reference);
}
}
if (dirty_state.stencil_front_write_mask && dirty_state.stencil_back_write_mask &&
stencil_front_write_mask == stencil_back_write_mask) {
dirty_state.stencil_front_write_mask = false;
dirty_state.stencil_back_write_mask = false;
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
stencil_front_write_mask);
} else {
if (dirty_state.stencil_front_write_mask) {
dirty_state.stencil_front_write_mask = false;
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront,
stencil_front_write_mask);
}
if (dirty_state.stencil_back_write_mask) {
dirty_state.stencil_back_write_mask = false;
cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, stencil_back_write_mask);
}
}
if (dirty_state.stencil_front_compare_mask && dirty_state.stencil_back_compare_mask &&
stencil_front_compare_mask == stencil_back_compare_mask) {
dirty_state.stencil_front_compare_mask = false;
dirty_state.stencil_back_compare_mask = false;
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
stencil_front_compare_mask);
} else {
if (dirty_state.stencil_front_compare_mask) {
dirty_state.stencil_front_compare_mask = false;
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront,
stencil_front_compare_mask);
}
if (dirty_state.stencil_back_compare_mask) {
dirty_state.stencil_back_compare_mask = false;
cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack,
stencil_back_compare_mask);
}
}
}
if (dirty_state.blend_constants) {
dirty_state.blend_constants = false;
cmdbuf.setBlendConstants(blend_constants);
}
if (dirty_state.color_write_masks) {
dirty_state.color_write_masks = false;
if (instance.IsDynamicColorWriteMaskSupported()) {
cmdbuf.setColorWriteMaskEXT(0, color_write_masks);
}
}
}
} // namespace Vulkan

View file

@ -7,6 +7,7 @@
#include <boost/container/static_vector.hpp>
#include "common/types.h"
#include "common/unique_function.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
@ -55,6 +56,219 @@ struct SubmitInfo {
}
};
using Viewports = boost::container::static_vector<vk::Viewport, AmdGpu::Liverpool::NumViewports>;
using Scissors = boost::container::static_vector<vk::Rect2D, AmdGpu::Liverpool::NumViewports>;
using ColorWriteMasks = std::array<vk::ColorComponentFlags, AmdGpu::Liverpool::NumColorBuffers>;
struct StencilOps {
vk::StencilOp fail_op{};
vk::StencilOp pass_op{};
vk::StencilOp depth_fail_op{};
vk::CompareOp compare_op{};
bool operator==(const StencilOps& other) const {
return fail_op == other.fail_op && pass_op == other.pass_op &&
depth_fail_op == other.depth_fail_op && compare_op == other.compare_op;
}
};
struct DynamicState {
struct {
bool viewports : 1;
bool scissors : 1;
bool depth_test_enabled : 1;
bool depth_write_enabled : 1;
bool depth_compare_op : 1;
bool depth_bounds_test_enabled : 1;
bool depth_bounds : 1;
bool depth_bias_enabled : 1;
bool depth_bias : 1;
bool stencil_test_enabled : 1;
bool stencil_front_ops : 1;
bool stencil_front_reference : 1;
bool stencil_front_write_mask : 1;
bool stencil_front_compare_mask : 1;
bool stencil_back_ops : 1;
bool stencil_back_reference : 1;
bool stencil_back_write_mask : 1;
bool stencil_back_compare_mask : 1;
bool blend_constants : 1;
bool color_write_masks : 1;
} dirty_state{};
Viewports viewports{};
Scissors scissors{};
bool depth_test_enabled{};
bool depth_write_enabled{};
vk::CompareOp depth_compare_op{};
bool depth_bounds_test_enabled{};
float depth_bounds_min{};
float depth_bounds_max{};
bool depth_bias_enabled{};
float depth_bias_constant{};
float depth_bias_clamp{};
float depth_bias_slope{};
bool stencil_test_enabled{};
StencilOps stencil_front_ops{};
u32 stencil_front_reference{};
u32 stencil_front_write_mask{};
u32 stencil_front_compare_mask{};
StencilOps stencil_back_ops{};
u32 stencil_back_reference{};
u32 stencil_back_write_mask{};
u32 stencil_back_compare_mask{};
float blend_constants[4]{};
ColorWriteMasks color_write_masks{};
/// Commits the dynamic state to the provided command buffer.
void Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf);
/// Invalidates all dynamic state to be flushed into the next command buffer.
void Invalidate() {
std::memset(&dirty_state, 0xFF, sizeof(dirty_state));
}
void SetViewports(const Viewports& viewports_) {
if (!std::ranges::equal(viewports, viewports_)) {
viewports = viewports_;
dirty_state.viewports = true;
}
}
void SetScissors(const Scissors& scissors_) {
if (!std::ranges::equal(scissors, scissors_)) {
scissors = scissors_;
dirty_state.scissors = true;
}
}
void SetDepthTestEnabled(const bool enabled) {
if (depth_test_enabled != enabled) {
depth_test_enabled = enabled;
dirty_state.depth_test_enabled = true;
}
}
void SetDepthWriteEnabled(const bool enabled) {
if (depth_write_enabled != enabled) {
depth_write_enabled = enabled;
dirty_state.depth_write_enabled = true;
}
}
void SetDepthCompareOp(const vk::CompareOp compare_op) {
if (depth_compare_op != compare_op) {
depth_compare_op = compare_op;
dirty_state.depth_compare_op = true;
}
}
void SetDepthBoundsTestEnabled(const bool enabled) {
if (depth_bounds_test_enabled != enabled) {
depth_bounds_test_enabled = enabled;
dirty_state.depth_bounds_test_enabled = true;
}
}
void SetDepthBounds(const float min, const float max) {
if (depth_bounds_min != min || depth_bounds_max != max) {
depth_bounds_min = min;
depth_bounds_max = max;
dirty_state.depth_bounds = true;
}
}
void SetDepthBiasEnabled(const bool enabled) {
if (depth_bias_enabled != enabled) {
depth_bias_enabled = enabled;
dirty_state.depth_bias_enabled = true;
}
}
void SetDepthBias(const float constant, const float clamp, const float slope) {
if (depth_bias_constant != constant || depth_bias_clamp != clamp ||
depth_bias_slope != slope) {
depth_bias_constant = constant;
depth_bias_clamp = clamp;
depth_bias_slope = slope;
dirty_state.depth_bias = true;
}
}
void SetStencilTestEnabled(const bool enabled) {
if (stencil_test_enabled != enabled) {
stencil_test_enabled = enabled;
dirty_state.stencil_test_enabled = true;
}
}
void SetStencilOps(const StencilOps& front_ops, const StencilOps& back_ops) {
if (stencil_front_ops != front_ops) {
stencil_front_ops = front_ops;
dirty_state.stencil_front_ops = true;
}
if (stencil_back_ops != back_ops) {
stencil_back_ops = back_ops;
dirty_state.stencil_back_ops = true;
}
}
void SetStencilReferences(const u32 front_reference, const u32 back_reference) {
if (stencil_front_reference != front_reference) {
stencil_front_reference = front_reference;
dirty_state.stencil_front_reference = true;
}
if (stencil_back_reference != back_reference) {
stencil_back_reference = back_reference;
dirty_state.stencil_back_reference = true;
}
}
void SetStencilWriteMasks(const u32 front_write_mask, const u32 back_write_mask) {
if (stencil_front_write_mask != front_write_mask) {
stencil_front_write_mask = front_write_mask;
dirty_state.stencil_front_write_mask = true;
}
if (stencil_back_write_mask != back_write_mask) {
stencil_back_write_mask = back_write_mask;
dirty_state.stencil_back_write_mask = true;
}
}
void SetStencilCompareMasks(const u32 front_compare_mask, const u32 back_compare_mask) {
if (stencil_front_compare_mask != front_compare_mask) {
stencil_front_compare_mask = front_compare_mask;
dirty_state.stencil_front_compare_mask = true;
}
if (stencil_back_compare_mask != back_compare_mask) {
stencil_back_compare_mask = back_compare_mask;
dirty_state.stencil_back_compare_mask = true;
}
}
void SetBlendConstants(const float blend_constants_[4]) {
if (!std::equal(blend_constants, std::end(blend_constants), blend_constants_)) {
std::memcpy(blend_constants, blend_constants_, sizeof(blend_constants));
dirty_state.blend_constants = true;
}
}
void SetColorWriteMasks(const ColorWriteMasks& color_write_masks_) {
if (!std::ranges::equal(color_write_masks, color_write_masks_)) {
color_write_masks = color_write_masks_;
dirty_state.color_write_masks = true;
}
}
};
class Scheduler {
public:
explicit Scheduler(const Instance& instance);
@ -81,6 +295,10 @@ public:
return render_state;
}
DynamicState& GetDynamicState() {
return dynamic_state;
}
/// Returns the current command buffer.
vk::CommandBuffer CommandBuffer() const {
return current_cmdbuf;
@ -125,6 +343,7 @@ private:
};
std::queue<PendingOp> pending_ops;
RenderState render_state;
DynamicState dynamic_state;
bool is_rendering = false;
tracy::VkCtxScope* profiler_scope{};
};