first try on dynamic rendering unused attachments

This commit is contained in:
CamilleLaVey
2026-09-27 01:15:26 -04:00
parent dfe0b7e7c7
commit 14b6160fcd
8 changed files with 94 additions and 20 deletions
@@ -88,6 +88,18 @@ bool ComputeAttachment0DualSourceBlend(const Maxwell& regs) {
: uses_dual_source(regs.blend);
}
bool WritesColorTarget(const Maxwell& regs, size_t index, bool static_masks) {
if (!static_masks || (index == 0 && regs.anti_alias_alpha_control.alpha_to_coverage != 0)) {
return true;
}
size_t mask_index = index;
if (regs.color_mask_common) {
mask_index = 0;
}
const auto& mask = regs.color_mask[mask_index];
return mask.R || mask.G || mask.B || mask.A;
}
void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
[](const auto& layout) {
@@ -101,7 +113,8 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
}
} // Anonymous namespace
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features,
u32 color_outputs) {
const Maxwell& regs = maxwell3d.regs;
const auto topology_ = maxwell3d.draw_manager.draw_state.topology;
@@ -141,8 +154,13 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
alpha_test_func.Assign(PackComparisonOp(test_func));
early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0);
depth_format.Assign(static_cast<u32>(regs.zeta.format));
const bool drop_unused = features.has_dynamic_rendering_unused_attachments;
const bool depth_used = regs.depth_test_enable || regs.depth_bounds_enable ||
regs.stencil_enable || extended_dynamic_state;
if (regs.zeta_enable != 0 && regs.zeta.Address() != 0 && (depth_used || !drop_unused)) {
depth_enabled.Assign(1);
depth_format.Assign(static_cast<u32>(regs.zeta.format));
}
y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
bool use_last_provoking_vertex = false;
@@ -179,8 +197,18 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
line_stipple_pattern = regs.line_stipple_params.pattern;
}
for (size_t i = 0; i < regs.rt.size(); ++i) {
color_formats[i] = static_cast<u8>(regs.rt[i].format);
const bool static_masks =
!features.has_extended_dynamic_state_3_blend && !features.has_color_write_enable;
const auto previous_formats = color_formats;
color_formats.fill(static_cast<u8>(Tegra::RenderTargetFormat::NONE));
const size_t num_targets = (std::min)(size_t{regs.rt_control.count}, color_formats.size());
for (size_t index = 0; index < num_targets; ++index) {
const bool used = ((color_outputs >> index) & 1) != 0 &&
WritesColorTarget(regs, index, static_masks);
if (regs.rt[index].Address() == 0 || (drop_unused && !used)) {
continue;
}
color_formats[index] = static_cast<u8>(regs.rt[index].format);
}
alpha_test_ref = 0;
if (regs.alpha_test_enabled != 0) {
@@ -251,11 +279,15 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {
if (maxwell3d.dirty.flags[Dirty::Blending]) {
if (maxwell3d.dirty.flags[Dirty::Blending] || color_formats != previous_formats) {
maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index);
auto& attachment = attachments[index];
attachment.raw = 0;
if (color_formats[index] == static_cast<u8>(Tegra::RenderTargetFormat::NONE)) {
continue;
}
attachment.Refresh(regs, index);
if (color_write_enable_dynamic && attachment.mask_r == 0 &&
attachment.mask_g == 0 && attachment.mask_b == 0 &&
attachment.mask_a == 0) {
@@ -38,6 +38,7 @@ struct DynamicFeatures {
bool has_provoking_vertex_first_mode;
bool has_provoking_vertex_last_mode;
bool has_provoking_vertex_tf_preserve;
bool has_dynamic_rendering_unused_attachments;
};
struct FixedPipelineState {
@@ -255,7 +256,8 @@ struct FixedPipelineState {
VideoCommon::TransformFeedbackState xfb_state;
void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features);
void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features,
u32 color_outputs);
size_t Hash() const noexcept;
@@ -62,7 +62,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 19;
constexpr u32 CACHE_VERSION = 20;
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
@@ -551,6 +551,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
device.SupportsProvokingVertexLastMode();
dynamic_features.has_provoking_vertex_tf_preserve =
device.SupportsTransformFeedbackProvokingVertexPreservation();
dynamic_features.has_dynamic_rendering_unused_attachments =
device.IsExtDynamicRenderingUnusedAttachmentsSupported();
}
PipelineCache::~PipelineCache() {
@@ -566,7 +568,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
current_pipeline = nullptr;
return nullptr;
}
graphics_key.state.Refresh(*maxwell3d, dynamic_features);
graphics_key.state.Refresh(*maxwell3d, dynamic_features, fragment_color_outputs);
if (current_pipeline) {
GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
@@ -244,24 +244,37 @@ void Scheduler::RequestComputeDispatchContext() {
void Scheduler::RelaxAttachmentOps(RenderingAttachments& attachments) const {
const bool load_op_none = device.IsLoadOpNoneSupported();
const bool drop_unused = device.IsExtDynamicRenderingUnusedAttachmentsSupported();
const auto relax = [&](VkRenderingAttachmentInfo& attachment, u32 bit) {
if (attachment.imageView == VK_NULL_HANDLE ||
attachment.resolveMode != VK_RESOLVE_MODE_NONE ||
if (attachment.imageView == VK_NULL_HANDLE) {
return true;
}
if (attachment.resolveMode != VK_RESOLVE_MODE_NONE ||
attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ||
attachment.storeOp != VK_ATTACHMENT_STORE_OP_STORE ||
(attachments_written & bit) != 0) {
return;
return false;
}
attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
if (load_op_none && (attachments_touched & bit) == 0) {
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;
}
return (attachments_touched & bit) == 0;
};
for (u32 slot = 0; slot < attachments.num_colors; ++slot) {
relax(attachments.colors[slot], 1u << slot);
VkRenderingAttachmentInfo& color = attachments.colors[slot];
if (!relax(color, 1u << slot)) {
continue;
}
if (drop_unused) {
color.imageView = VK_NULL_HANDLE;
} else if (load_op_none) {
color.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;
}
}
const bool depth_unused = relax(attachments.depth, DEPTH_ATTACHMENT_BIT);
const bool stencil_unused = relax(attachments.stencil, STENCIL_ATTACHMENT_BIT);
if (drop_unused && depth_unused && stencil_unused) {
attachments.depth.imageView = VK_NULL_HANDLE;
attachments.stencil.imageView = VK_NULL_HANDLE;
}
relax(attachments.depth, DEPTH_ATTACHMENT_BIT);
relax(attachments.stencil, STENCIL_ATTACHMENT_BIT);
}
void Scheduler::PublishComputeWrites() {
+11
View File
@@ -49,6 +49,7 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
return last_shaders_valid;
}
dirty[VideoCommon::Dirty::Shaders] = false;
fragment_color_outputs = 0;
const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) {
@@ -78,6 +79,9 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
}
shader_infos[index] = shader_info;
unique_hashes[index] = shader_info->unique_hash;
if (program == Tegra::Engines::Maxwell3D::Regs::ShaderType::Pixel) {
fragment_color_outputs = shader_info->color_outputs;
}
}
last_shaders_valid = true;
return true;
@@ -241,6 +245,13 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
info->unique_hash = env.CalculateHash();
info->size_bytes = env.ReadSizeBytes();
}
if (env.ShaderStage() == Shader::Stage::Fragment) {
for (u32 target = 0; target < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++target) {
if (env.SPH().ps.HasOutputComponents(target)) {
info->color_outputs |= 1U << target;
}
}
}
const size_t size_bytes{info->size_bytes};
const ShaderInfo* const result{info.get()};
Register(std::move(info), cpu_addr, size_bytes);
+2
View File
@@ -36,6 +36,7 @@ class GenericEnvironment;
struct ShaderInfo {
u64 unique_hash{};
size_t size_bytes{};
u32 color_outputs{};
};
class ShaderCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
@@ -98,6 +99,7 @@ protected:
std::array<const ShaderInfo*, NUM_PROGRAMS> shader_infos{};
bool last_shaders_valid = false;
u32 fragment_color_outputs{};
private:
/// @brief Tries to obtain a cached shader starting in a given address
@@ -1247,6 +1247,12 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.descriptor_buffer, features.descriptor_buffer,
VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
extensions.dynamic_rendering_unused_attachments =
features.dynamic_rendering_unused_attachments.dynamicRenderingUnusedAttachments;
RemoveExtensionFeatureIfUnsuitable(extensions.dynamic_rendering_unused_attachments,
features.dynamic_rendering_unused_attachments,
VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
+7 -1
View File
@@ -61,6 +61,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \
FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
FEATURE(EXT, DescriptorBuffer, DESCRIPTOR_BUFFER, descriptor_buffer) \
FEATURE(EXT, DynamicRenderingUnusedAttachments, DYNAMIC_RENDERING_UNUSED_ATTACHMENTS, \
dynamic_rendering_unused_attachments) \
FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
@@ -670,11 +672,15 @@ FN_MAX_LIMIT_LIST
return extensions.depth_clip_control;
}
/// Returns true if the device supports VK_EXT_depth_bias_control.
bool IsLoadOpNoneSupported() const {
return extensions.load_store_op_none || extensions.load_store_op_none_ext;
}
bool IsExtDynamicRenderingUnusedAttachmentsSupported() const {
return extensions.dynamic_rendering_unused_attachments;
}
/// Returns true if the device supports VK_EXT_depth_bias_control.
bool IsExtDepthBiasControlSupported() const {
return extensions.depth_bias_control;
}