mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
470 lines
21 KiB
C++
470 lines
21 KiB
C++
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
|
|
|
#include "common/assert.h"
|
|
#include "common/common.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/profiler.h"
|
|
#include "common/stringUtils.h"
|
|
#include "common/threads.h"
|
|
#include "graphics/guest_gpu/gpu_defs.h"
|
|
#include "graphics/guest_gpu/hardwareContext.h"
|
|
#include "graphics/guest_gpu/tile.h"
|
|
#include "graphics/host_gpu/graphicContext.h"
|
|
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
|
#include "graphics/host_gpu/renderer/debug.h"
|
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
|
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
|
#include "graphics/host_gpu/renderer/render.h"
|
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
|
#include "graphics/host_gpu/vulkanCommon.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cmath>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <limits>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
[[noreturn]] static void DepthFatal(const char* format, ...) {
|
|
std::fputs("Depth target fatal: ", stderr);
|
|
va_list args;
|
|
va_start(args, format);
|
|
std::vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
std::fputc('\n', stderr);
|
|
std::fflush(stderr);
|
|
EXIT("unsupported render state; details were printed above\n");
|
|
}
|
|
|
|
static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t op_value) {
|
|
if (write_mask == 0) {
|
|
return vk::StencilOp::eKeep;
|
|
}
|
|
switch (static_cast<Prospero::StencilOp>(value)) {
|
|
case Prospero::StencilOp::kKeep: return vk::StencilOp::eKeep;
|
|
case Prospero::StencilOp::kZero: return vk::StencilOp::eZero;
|
|
case Prospero::StencilOp::kReplaceTest: return vk::StencilOp::eReplace;
|
|
case Prospero::StencilOp::kReplaceOp:
|
|
if ((op_value & write_mask) == 0) {
|
|
return vk::StencilOp::eZero;
|
|
}
|
|
return vk::StencilOp::eReplace;
|
|
case Prospero::StencilOp::kAddClamp: return vk::StencilOp::eIncrementAndClamp;
|
|
case Prospero::StencilOp::kSubClamp: return vk::StencilOp::eDecrementAndClamp;
|
|
case Prospero::StencilOp::kInvert: return vk::StencilOp::eInvert;
|
|
case Prospero::StencilOp::kAddWrap: return vk::StencilOp::eIncrementAndWrap;
|
|
case Prospero::StencilOp::kSubWrap: return vk::StencilOp::eDecrementAndWrap;
|
|
case Prospero::StencilOp::kXor:
|
|
if ((write_mask & op_value) == 0) {
|
|
return vk::StencilOp::eKeep;
|
|
}
|
|
if ((write_mask & ~op_value) != 0) {
|
|
DepthFatal("unsupported stencil XOR operands: write mask=0x%02" PRIx8
|
|
", operation value=0x%02" PRIx8,
|
|
write_mask, op_value);
|
|
}
|
|
return vk::StencilOp::eInvert;
|
|
default: DepthFatal("unsupported stencil operation: 0x%02" PRIx8, value);
|
|
}
|
|
}
|
|
|
|
static vk::StencilOpState ConvertStencilState(
|
|
uint8_t compare, const std::array<uint8_t, 3>& operations, uint8_t op_value,
|
|
const vk::StencilOpState& state) {
|
|
const auto test_value = state.reference;
|
|
auto reference = test_value;
|
|
auto required_bits = state.compareMask;
|
|
if (compare == static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
|
compare == static_cast<uint8_t>(vk::CompareOp::eNever)) {
|
|
required_bits = 0;
|
|
}
|
|
std::array<vk::StencilOp, 3> converted {};
|
|
for (size_t i = 0; i < operations.size(); i++) {
|
|
converted[i] = ConvertStencilOp(operations[i], state.writeMask, op_value);
|
|
if (converted[i] != vk::StencilOp::eReplace) {
|
|
continue;
|
|
}
|
|
auto replacement = test_value;
|
|
if (static_cast<Prospero::StencilOp>(operations[i]) == Prospero::StencilOp::kReplaceOp) {
|
|
replacement = op_value;
|
|
}
|
|
if (((reference ^ replacement) & required_bits & state.writeMask) != 0) {
|
|
DepthFatal("unsupported stencil replacement: compare=%u, compare mask=0x%02" PRIx32
|
|
", write mask=0x%02" PRIx32 ", operation value=0x%02" PRIx8
|
|
", test value=0x%02" PRIx32,
|
|
compare, state.compareMask, state.writeMask, op_value, test_value);
|
|
}
|
|
// Vulkan shares one reference between comparison and every replacement on this face.
|
|
reference = (reference & ~state.writeMask) | (replacement & state.writeMask);
|
|
required_bits |= state.writeMask;
|
|
}
|
|
return {converted[0], converted[1], converted[2], static_cast<vk::CompareOp>(compare),
|
|
state.compareMask, state.writeMask, reference};
|
|
}
|
|
|
|
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const CommandBuffer& buffer,
|
|
const DepthFormatPolicy& policy,
|
|
bool has_stencil,
|
|
uint32_t samples) {
|
|
auto& graphics = buffer.GetGraphics();
|
|
const auto required_samples = vulkan_sample_count(samples);
|
|
const auto supports = [&](vk::Format format) {
|
|
vk::ImageFormatProperties properties {};
|
|
return format != vk::Format::eUndefined &&
|
|
graphics.GetImageFormatProperties(
|
|
format, vk::ImageType::e2D, vk::ImageTiling::eOptimal, DepthTargetImageUsage(),
|
|
vk::ImageCreateFlags {}, &properties) == vk::Result::eSuccess &&
|
|
static_cast<bool>(properties.sampleCounts & required_samples);
|
|
};
|
|
if (!has_stencil) {
|
|
return supports(policy.depth_attachment_format) ? policy.depth_attachment_format
|
|
: vk::Format::eUndefined;
|
|
}
|
|
for (const auto format: policy.stencil_attachment_formats) {
|
|
if (supports(format)) {
|
|
return format;
|
|
}
|
|
}
|
|
return vk::Format::eUndefined;
|
|
}
|
|
|
|
static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
|
|
const HW::DepthRenderTarget& z,
|
|
bool write_buffer = false) {
|
|
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
|
const auto depth_address = write_buffer ? z.z_write_base_addr : z.z_read_base_addr;
|
|
const auto stencil_address =
|
|
write_buffer ? z.stencil_write_base_addr : z.stencil_read_base_addr;
|
|
if (!z.z_info.HasValidTextureCompatibility() ||
|
|
!z.stencil_info.HasValidTextureCompatibility()) {
|
|
DepthFatal("invalid PS5 depth texture-compatibility encoding");
|
|
}
|
|
const bool has_htile = z.z_info.htile_acceleration;
|
|
const bool unsupported_shading_rate_encoding =
|
|
z.shading_rate_encoding > 1 || (z.shading_rate_encoding != 0 && !has_htile);
|
|
const auto samples = render_sample_count(z.z_info.num_samples);
|
|
if (samples == 0) {
|
|
DepthFatal("unsupported depth fragment count: %u", z.z_info.num_samples);
|
|
}
|
|
const bool htile_stencil_compat = depth_htile_stencil_acceleration_compatible(
|
|
has_stencil, has_htile, z.stencil_info.htile_stencil_disabled);
|
|
const auto view = ResolveTargetViewInfo(z.depth_view.slice_start, z.depth_view.slice_max);
|
|
switch (view.type) {
|
|
case TargetViewType::Image2D:
|
|
case TargetViewType::Image2DArray: break;
|
|
case TargetViewType::Unsupported:
|
|
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
|
|
z.depth_view.slice_max);
|
|
}
|
|
// EXPCLEAR permits an HTile acceleration state; the host attachment is already expanded.
|
|
if (z.z_info.max_mip_level != 0 ||
|
|
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
|
|
depth_address == 0 || (depth_address & 0xffffu) != 0) {
|
|
DepthFatal("unsupported depth register state");
|
|
}
|
|
if (has_stencil) {
|
|
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
|
|
stencil_address == 0 || (stencil_address & 0xffffu) != 0) {
|
|
DepthFatal("unsupported stencil attachment state");
|
|
}
|
|
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
|
|
DepthFatal("stencil state without an active stencil attachment");
|
|
}
|
|
if (has_htile) {
|
|
if (z.htile_data_base_addr == 0 || (z.htile_data_base_addr & 0x7fffu) != 0) {
|
|
DepthFatal("invalid HTile metadata address");
|
|
}
|
|
}
|
|
if (!z.size.valid) {
|
|
DepthFatal("missing depth extent");
|
|
}
|
|
const uint32_t width = static_cast<uint32_t>(z.size.x_max) + 1u;
|
|
const uint32_t height = static_cast<uint32_t>(z.size.y_max) + 1u;
|
|
if (width > 16384 || height > 16384) {
|
|
DepthFatal("invalid depth extent");
|
|
}
|
|
const auto* policy = FindDepthFormatPolicy(z.z_info.format);
|
|
if (policy == nullptr) {
|
|
DepthFatal("unsupported depth/stencil format pair");
|
|
}
|
|
const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil);
|
|
const auto format = ResolveHostDepthAttachmentFormat(buffer, *policy, has_stencil, samples);
|
|
if (format == vk::Format::eUndefined) {
|
|
DepthFatal("no host depth/stencil format supports required usage for %s",
|
|
vk::to_string(ideal_format).c_str());
|
|
}
|
|
const auto guest_format = policy->guest_format;
|
|
const uint32_t bytes = policy->bytes_per_element;
|
|
const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
|
|
TileSizeAlign depth_size {};
|
|
TileSizeAlign stencil_size {};
|
|
TileSizeAlign htile_size {};
|
|
if (!TileGetDepthSize(width, height, 0, z.z_info.format, z.stencil_info.format, has_htile,
|
|
stencil_size, htile_size, depth_size, z.z_info.num_samples) ||
|
|
depth_size.align != 65536 || depth_size.size == 0 ||
|
|
(has_stencil != (stencil_size.align == 65536 && stencil_size.size != 0)) ||
|
|
(has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
|
|
DepthFatal("unsupported depth/stencil/HTile footprint");
|
|
}
|
|
if (depth_size.size > UINT64_MAX / view.image_layers ||
|
|
stencil_size.size > UINT64_MAX / view.image_layers ||
|
|
htile_size.size > UINT64_MAX / view.image_layers) {
|
|
DepthFatal("layered depth footprint overflow");
|
|
}
|
|
const auto depth_backing_size = depth_size.size * view.image_layers;
|
|
const auto stencil_backing_size = stencil_size.size * view.image_layers;
|
|
const auto htile_backing_size = htile_size.size * view.image_layers;
|
|
if (!GuestRange {depth_address, depth_backing_size}.Valid() ||
|
|
(has_stencil && !GuestRange {stencil_address, stencil_backing_size}.Valid()) ||
|
|
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
|
|
DepthFatal("layered depth backing range is invalid");
|
|
}
|
|
TextureCache::ImageDesc desc {};
|
|
desc.type = TextureCache::BindingType::DepthTarget;
|
|
desc.info.data = {depth_address, depth_backing_size};
|
|
desc.info.stencil =
|
|
has_stencil ? GuestRange {stencil_address, stencil_backing_size} : GuestRange {};
|
|
desc.info.pixel_format = format;
|
|
desc.info.guest_format = guest_format;
|
|
desc.info.type = Prospero::ImageType::kColor2D;
|
|
desc.info.extent = {width, height, 1};
|
|
desc.info.resources = {1, view.image_layers};
|
|
desc.info.pitch = pitch;
|
|
desc.info.bytes_per_block = bytes;
|
|
desc.info.samples = samples;
|
|
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
|
desc.info.UpdateSize();
|
|
desc.info.metadata.range =
|
|
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
|
|
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
|
desc.info.metadata.stencil_compressed =
|
|
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
|
desc.view_info.format = format;
|
|
desc.view_info.type =
|
|
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
|
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
|
|
desc.view_info.base_level = 0;
|
|
desc.view_info.level_count = 1;
|
|
desc.view_info.base_layer = view.base_layer;
|
|
desc.view_info.layer_count = view.layer_count;
|
|
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
|
return desc;
|
|
}
|
|
|
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
|
|
KYTY_PROFILER_FUNCTION();
|
|
const auto& hw = buffer.GetRegisters();
|
|
const auto& z = hw.GetDepthRenderTarget();
|
|
const auto& rc = hw.GetRenderControl();
|
|
const auto& dc = hw.GetDepthControl();
|
|
const auto& sc = hw.GetStencilControl();
|
|
const auto& sm = hw.GetStencilMask();
|
|
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
|
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
|
|
rc.depth_clear_enable || rc.copy_depth_to_color;
|
|
const bool stencil_active =
|
|
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
|
if (!depth_active && !stencil_active) {
|
|
return;
|
|
}
|
|
const bool attachment_unbound =
|
|
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
|
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
|
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
|
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
|
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
|
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
|
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
|
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
|
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
|
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
|
z.htile_data_base_addr == 0 &&
|
|
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
|
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
|
// presence alone must not manufacture a depth attachment.
|
|
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
|
z.size.y_max == 0;
|
|
if (attachment_unbound) {
|
|
static std::atomic_bool logged = false;
|
|
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
|
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
|
}
|
|
return;
|
|
}
|
|
if (rc.copy_stencil_to_color ||
|
|
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
|
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
|
(has_stencil && !z.depth_view.stencil_write_disable &&
|
|
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
|
|
DepthFatal("unsupported depth register state");
|
|
}
|
|
if (rc.copy_depth_to_color &&
|
|
(z.z_info.num_samples != 0 || rc.copy_sample != 0 ||
|
|
z.z_info.format != Prospero::DepthFormat::kZ32F ||
|
|
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || dc.stencil_enable ||
|
|
rc.depth_clear_enable || rc.stencil_clear_enable)) {
|
|
DepthFatal("unsupported depth-to-color copy state");
|
|
}
|
|
r.desc = MakeDepthTargetDesc(buffer, z);
|
|
r.depth_clear_enable = rc.depth_clear_enable;
|
|
r.depth_clear_value = hw.GetDepthClearValue();
|
|
r.depth_test_enable = dc.z_enable;
|
|
r.depth_write_enable = r.depth_test_enable && dc.z_write_enable &&
|
|
!z.depth_view.depth_write_disable && !r.depth_clear_enable;
|
|
r.depth_compare_op = static_cast<vk::CompareOp>(dc.zfunc);
|
|
|
|
r.depth_bounds_test_enable = dc.depth_bounds_enable;
|
|
|
|
r.stencil_clear_enable =
|
|
has_stencil && rc.stencil_clear_enable && !z.depth_view.stencil_write_disable;
|
|
r.stencil_clear_value = hw.GetStencilClearValue();
|
|
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
|
if (r.stencil_test_enable) {
|
|
const bool stencil_ops_disabled =
|
|
rc.stencil_clear_enable || z.depth_view.stencil_write_disable;
|
|
const uint8_t front_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask;
|
|
const uint8_t back_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask_bf;
|
|
if (dc.stencilfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
|
(dc.backface_enable &&
|
|
dc.stencilfunc_bf > static_cast<uint8_t>(vk::CompareOp::eAlways))) {
|
|
DepthFatal("unsupported stencil compare state");
|
|
}
|
|
r.stencil_front = ConvertStencilState(
|
|
dc.stencilfunc, {sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail},
|
|
sm.stencil_opval, {.compareMask = sm.stencil_mask, .writeMask = front_write_mask,
|
|
.reference = sm.stencil_testval});
|
|
if (dc.backface_enable) {
|
|
r.stencil_back = ConvertStencilState(
|
|
dc.stencilfunc_bf, {sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf},
|
|
sm.stencil_opval_bf, {.compareMask = sm.stencil_mask_bf, .writeMask = back_write_mask,
|
|
.reference = sm.stencil_testval_bf});
|
|
} else {
|
|
r.stencil_back = r.stencil_front;
|
|
}
|
|
}
|
|
auto& cache = m_context.GetTextureCache();
|
|
r.image_id = cache.FindImage(r.desc);
|
|
BindRenderTarget(r.image_id);
|
|
}
|
|
|
|
bool RenderExecutor::DepthStencilCopy(CommandBuffer& buffer) {
|
|
const auto& hw = buffer.GetRegisters();
|
|
const auto& z = hw.GetDepthRenderTarget();
|
|
const auto& override = hw.GetDepthRenderOverride();
|
|
if (hw.GetColorControl().mode != 0) {
|
|
return false;
|
|
}
|
|
const bool depth_copy = override.force_z_dirty && override.force_z_valid &&
|
|
z.z_info.format != Prospero::DepthFormat::kInvalid &&
|
|
z.z_read_base_addr != 0 && z.z_write_base_addr != 0 &&
|
|
z.z_read_base_addr != z.z_write_base_addr;
|
|
const bool stencil_copy = override.force_stencil_dirty && override.force_stencil_valid &&
|
|
z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
|
|
z.stencil_read_base_addr != 0 && z.stencil_write_base_addr != 0 &&
|
|
z.stencil_read_base_addr != z.stencil_write_base_addr;
|
|
if (!depth_copy && !stencil_copy) {
|
|
return false;
|
|
}
|
|
|
|
auto read_desc = MakeDepthTargetDesc(buffer, z);
|
|
auto write_desc = MakeDepthTargetDesc(buffer, z, true);
|
|
auto& cache = m_context.GetTextureCache();
|
|
const auto read_id = cache.FindImage(read_desc);
|
|
BindRenderTarget(read_id);
|
|
const auto write_id = cache.FindImage(write_desc);
|
|
BindRenderTarget(write_id);
|
|
cache.UpdateImage(read_id);
|
|
cache.UpdateImage(write_id);
|
|
cache.MarkGpuWritten(write_id);
|
|
auto& source = cache.GetImage(read_id);
|
|
auto& destination = cache.GetImage(write_id);
|
|
EXIT_IF(read_id == write_id || source.backing.format != destination.backing.format);
|
|
|
|
auto& scheduler = m_context.GetCommandScheduler();
|
|
scheduler.EndRendering();
|
|
const auto command = scheduler.Current().Handle();
|
|
const ImageSubresourceRange range {read_desc.view_info.base_level, 1,
|
|
read_desc.view_info.base_layer,
|
|
read_desc.view_info.layer_count};
|
|
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
|
range, command);
|
|
destination.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
|
range, command);
|
|
std::array<vk::ImageCopy, 2> regions {};
|
|
uint32_t count = 0;
|
|
for (const auto aspect: {vk::ImageAspectFlagBits::eDepth, vk::ImageAspectFlagBits::eStencil}) {
|
|
if ((aspect == vk::ImageAspectFlagBits::eDepth && !depth_copy) ||
|
|
(aspect == vk::ImageAspectFlagBits::eStencil && !stencil_copy)) {
|
|
continue;
|
|
}
|
|
auto& region = regions[count++];
|
|
region.srcSubresource = {aspect, range.base_level, range.base_layer,
|
|
range.layer_count};
|
|
region.dstSubresource = region.srcSubresource;
|
|
region.extent = write_desc.info.extent;
|
|
}
|
|
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
|
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
|
count, regions.data());
|
|
return true;
|
|
}
|
|
|
|
vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
|
const auto format = desc.view_info.format;
|
|
if (format == vk::Format::eUndefined) {
|
|
return {};
|
|
}
|
|
|
|
const auto available = ImageViewOps::DepthAspectMask(format);
|
|
vk::ImageAspectFlags writes = load_clear_aspects & available;
|
|
if ((available & vk::ImageAspectFlagBits::eDepth) &&
|
|
depth_write_enable) {
|
|
writes |= vk::ImageAspectFlagBits::eDepth;
|
|
}
|
|
if (!(available & vk::ImageAspectFlagBits::eStencil)) {
|
|
return writes;
|
|
}
|
|
|
|
const auto face_writes = [&](const vk::StencilOpState& state) {
|
|
if (state.writeMask == 0) {
|
|
return false;
|
|
}
|
|
bool can_pass = state.compareOp != vk::CompareOp::eNever;
|
|
bool can_fail = state.compareOp != vk::CompareOp::eAlways;
|
|
if (state.compareMask == 0) {
|
|
switch (state.compareOp) {
|
|
case vk::CompareOp::eEqual:
|
|
case vk::CompareOp::eLessOrEqual:
|
|
case vk::CompareOp::eGreaterOrEqual:
|
|
case vk::CompareOp::eAlways:
|
|
can_pass = true;
|
|
can_fail = false;
|
|
break;
|
|
case vk::CompareOp::eNever:
|
|
case vk::CompareOp::eLess:
|
|
case vk::CompareOp::eGreater:
|
|
case vk::CompareOp::eNotEqual:
|
|
can_pass = false;
|
|
can_fail = true;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
const bool depth_pass = !depth_test_enable || depth_compare_op != vk::CompareOp::eNever;
|
|
const bool depth_fail = depth_test_enable && depth_compare_op != vk::CompareOp::eAlways;
|
|
return (can_fail && state.failOp != vk::StencilOp::eKeep) ||
|
|
(can_pass && depth_pass && state.passOp != vk::StencilOp::eKeep) ||
|
|
(can_pass && depth_fail && state.depthFailOp != vk::StencilOp::eKeep);
|
|
};
|
|
if (stencil_test_enable && (face_writes(stencil_front) || face_writes(stencil_back))) {
|
|
writes |= vk::ImageAspectFlagBits::eStencil;
|
|
}
|
|
return writes;
|
|
}
|
|
|
|
} // namespace Libs::Graphics
|