mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
Implement 0x12 CopyData, sampled-to-depth fix, stencil detailing
This commit is contained in:
@@ -1207,6 +1207,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
(vaddr & (TRACKER_PAGE_SIZE - 1)) == 0 && (size & (TRACKER_PAGE_SIZE - 1)) == 0);
|
||||
}
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
const bool cpu_dirty = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||
auto use_coherent_guest_backing = [&](const CachedBuffer& overlap) {
|
||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||
const auto dirty_ranges = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||
@@ -1219,7 +1220,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
// A clean/CPU-current image range may coexist with a partial cached-buffer view because
|
||||
// guest backing is authoritative. The PS5 detiler consumes that backing at the existing
|
||||
// Tiler seam.
|
||||
return BufferImageCopySource {nullptr, 0, vaddr, size, true};
|
||||
return BufferImageCopySource {nullptr, 0, vaddr, size, true, cpu_dirty};
|
||||
};
|
||||
auto it = m_buffers.upper_bound(vaddr);
|
||||
if (it == m_buffers.begin()) {
|
||||
@@ -1227,7 +1228,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
PageOverlaps(vaddr, size, it->second->vaddr, it->second->size)) {
|
||||
return use_coherent_guest_backing(*it->second);
|
||||
}
|
||||
return {nullptr, 0, vaddr, size, true};
|
||||
return {nullptr, 0, vaddr, size, true, cpu_dirty};
|
||||
}
|
||||
--it;
|
||||
auto& cached = *it->second;
|
||||
@@ -1241,7 +1242,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
PageOverlaps(vaddr, size, next->second->vaddr, next->second->size)) {
|
||||
return use_coherent_guest_backing(*next->second);
|
||||
}
|
||||
return {nullptr, 0, vaddr, size, true};
|
||||
return {nullptr, 0, vaddr, size, true, cpu_dirty};
|
||||
}
|
||||
if (cached.ctx == nullptr || cached.buffer == nullptr || cached.buffer->buffer == nullptr) {
|
||||
EXIT("BufferCache: inconsistent image source, addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
@@ -1250,7 +1251,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
static_cast<const void*>(cached.buffer.get()));
|
||||
}
|
||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||
const bool cpu_modified = cpu_dirty;
|
||||
if (gpu_modified && cpu_modified) {
|
||||
EXIT("BufferCache: image source is both CPU- and GPU-modified, addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 "\n",
|
||||
@@ -1333,7 +1334,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
|
||||
" size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
return {cached.buffer.get(), offset, vaddr, size, true};
|
||||
return {cached.buffer.get(), offset, vaddr, size, true, cpu_dirty};
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -26,6 +26,9 @@ struct BufferImageCopySource {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
bool cpu_current = false;
|
||||
// True when the guest range was CPU-dirty before coherence resolution. ObtainBufferForImage
|
||||
// may consume that tracker state while publishing the same bytes to a cached buffer.
|
||||
bool cpu_dirty = false;
|
||||
};
|
||||
|
||||
struct BufferCacheRange {
|
||||
|
||||
@@ -340,6 +340,8 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
|
||||
(r->stencil_test_enable &&
|
||||
(stencil_face_accesses_attachment(r->stencil_static_front, r->stencil_dynamic_front) ||
|
||||
stencil_face_accesses_attachment(r->stencil_static_back, r->stencil_dynamic_back)));
|
||||
info.stencil_htile_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.tile_stencil_disable;
|
||||
r->vulkan_buffer = cache->FindDepthTarget(buffer, g_render_ctx->GetGraphicCtx(), info);
|
||||
if (meta_clear && !cache->TouchMeta(z.htile_data_base_addr, z.depth_view.slice_start, false)) {
|
||||
DepthFatal("failed to consume HTile clear state");
|
||||
|
||||
@@ -86,6 +86,7 @@ struct DepthTargetInfo {
|
||||
bool depth_load_clear = false;
|
||||
bool stencil_load_clear = false;
|
||||
bool stencil_access = false;
|
||||
bool stencil_htile_compressed = false;
|
||||
};
|
||||
|
||||
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_64_64, Unsupported };
|
||||
@@ -171,6 +172,7 @@ struct VideoOutPixelFormatInfo {
|
||||
}
|
||||
|
||||
enum class DepthOverlap : uint8_t { None, RetireSampled, Unsupported };
|
||||
enum class DepthTransitionSource : uint8_t { None, Guest, Native };
|
||||
enum class RenderTargetOverlap : uint8_t { None, RetireSampled, RetireTarget, Unsupported };
|
||||
enum class SampledOverlap : uint8_t { None, ReadOnlyAlias, Unsupported };
|
||||
enum class StorageSampledOverlap : uint8_t { None, ExactImage, Unsupported };
|
||||
@@ -178,6 +180,19 @@ enum class HostWriteOverlap : uint8_t { None, InvalidateImage, Unsupported };
|
||||
enum class BufferImageAlias : uint8_t { None, VideoOutWrite, RenderTargetWrite, Unsupported };
|
||||
enum class MetaImageOverlap : uint8_t { RetainSampled, RetireTarget, Unsupported };
|
||||
|
||||
[[nodiscard]] inline constexpr DepthTransitionSource
|
||||
SelectDepthTransitionSource(bool depth_load_clear, bool sampled_native_available,
|
||||
bool sampled_cpu_dirty, bool sampled_buffer_modified,
|
||||
bool buffer_overlap, bool buffer_cpu_dirty) noexcept {
|
||||
if (depth_load_clear) {
|
||||
return DepthTransitionSource::None;
|
||||
}
|
||||
return sampled_native_available && !sampled_cpu_dirty && !sampled_buffer_modified &&
|
||||
!(buffer_overlap && buffer_cpu_dirty)
|
||||
? DepthTransitionSource::Native
|
||||
: DepthTransitionSource::Guest;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline MetaImageOverlap ClassifyMetaImageOverlap(bool sampled, bool render_target,
|
||||
bool gpu_modified,
|
||||
bool buffer_modified) {
|
||||
@@ -281,12 +296,17 @@ enum class MetaImageOverlap : uint8_t { RetainSampled, RetireTarget, Unsupported
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool CanLoadStencilAttachment(const DepthTargetInfo& target,
|
||||
bool stencil_initialized) {
|
||||
bool stencil_initialized) {
|
||||
const bool has_stencil = target.stencil_address != 0 || target.stencil_size != 0;
|
||||
return !has_stencil || !target.stencil_access || target.stencil_load_clear ||
|
||||
stencil_initialized;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool CanLoadRawStencilPlane(const DepthTargetInfo& target) {
|
||||
const bool has_stencil = target.stencil_address != 0 || target.stencil_size != 0;
|
||||
return has_stencil && !target.stencil_htile_compressed;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IsDepthTargetRangeCompatible(const DepthTargetInfo& target,
|
||||
uint64_t address, uint64_t size) {
|
||||
if (address == 0 || size == 0 || address > UINT64_MAX - size) {
|
||||
@@ -414,6 +434,22 @@ ClassifyBufferImageAlias(uint64_t buffer_address, uint64_t buffer_size, uint64_t
|
||||
return DepthOverlap::None;
|
||||
}
|
||||
const bool has_stencil = depth.stencil_address != 0 || depth.stencil_size != 0;
|
||||
const bool exact_depth_format =
|
||||
sampled.format == depth.guest_format &&
|
||||
((depth.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) &&
|
||||
depth.format == VK_FORMAT_D16_UNORM && depth.bytes_per_element == 2) ||
|
||||
(depth.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) &&
|
||||
depth.format == VK_FORMAT_D32_SFLOAT && depth.bytes_per_element == 4));
|
||||
const bool exact_depth_load =
|
||||
!has_stencil && !depth.depth_load_clear && sampled.address == depth.address &&
|
||||
sampled.size == depth.size && sampled.width == depth.width && sampled.height == depth.height &&
|
||||
sampled.pitch == depth.pitch && sampled.base_level == 0 && sampled.levels == 1 &&
|
||||
sampled.view_levels == 1 && sampled.tile == depth.tile_mode && sampled.depth == 1 &&
|
||||
sampled.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
sampled.base_array == 0 && exact_depth_format;
|
||||
if (!sampled_gpu_modified && exact_depth_load) {
|
||||
return DepthOverlap::RetireSampled;
|
||||
}
|
||||
if (sampled.address == depth.address && !sampled_gpu_modified && depth.depth_load_clear &&
|
||||
(!has_stencil ||
|
||||
(depth.stencil_address != 0 && depth.stencil_size != 0 && depth.stencil_load_clear))) {
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace Libs::Graphics {
|
||||
switch (swizzle) {
|
||||
case DstSel(4, 4, 4, 4): return VulkanImage::VIEW_DEPTH_TEXTURE;
|
||||
case DstSel(4, 0, 0, 0): return VulkanImage::VIEW_R000;
|
||||
case DstSel(4, 0, 0, 1): return VulkanImage::VIEW_R001;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,10 @@ void RenderDispatchDirect(uint64_t submit_id, CommandBuffer* buffer, HW::Context
|
||||
void GraphicsRenderInit();
|
||||
void GraphicsRenderCreateContext();
|
||||
|
||||
[[nodiscard]] bool GraphicsScaleReferenceClock(uint64_t host_ticks, uint64_t host_frequency,
|
||||
uint64_t* value);
|
||||
[[nodiscard]] uint64_t GraphicsRenderReadReferenceClock();
|
||||
|
||||
[[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x,
|
||||
uint32_t group_y, uint32_t group_z, uint32_t mode,
|
||||
ShaderBufferResource* descriptor,
|
||||
|
||||
@@ -14,11 +14,47 @@
|
||||
#include "libs/errno.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
constexpr int GRAPHICS_EVENT_QUEUED_GRAPHICS_INTERRUPT = 0x00;
|
||||
constexpr int GRAPHICS_EVENT_EOP = 0x40;
|
||||
constexpr uint64_t GRAPHICS_REFERENCE_CLOCK_FREQUENCY = 100000000;
|
||||
|
||||
bool GraphicsScaleReferenceClock(uint64_t host_ticks, uint64_t host_frequency, uint64_t* value) {
|
||||
if (host_frequency == 0 || value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto whole_seconds = host_ticks / host_frequency;
|
||||
const auto remainder = host_ticks % host_frequency;
|
||||
constexpr auto MAX_VALUE = std::numeric_limits<uint64_t>::max();
|
||||
if (whole_seconds > MAX_VALUE / GRAPHICS_REFERENCE_CLOCK_FREQUENCY ||
|
||||
remainder > MAX_VALUE / GRAPHICS_REFERENCE_CLOCK_FREQUENCY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto whole_value = whole_seconds * GRAPHICS_REFERENCE_CLOCK_FREQUENCY;
|
||||
const auto fractional_value =
|
||||
(remainder * GRAPHICS_REFERENCE_CLOCK_FREQUENCY) / host_frequency;
|
||||
if (whole_value > MAX_VALUE - fractional_value) {
|
||||
return false;
|
||||
}
|
||||
*value = whole_value + fractional_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t GraphicsRenderReadReferenceClock() {
|
||||
const auto host_frequency = LibKernel::KernelGetTscFrequency();
|
||||
const auto host_ticks = LibKernel::KernelReadTsc();
|
||||
uint64_t value = 0;
|
||||
if (!GraphicsScaleReferenceClock(host_ticks, host_frequency, &value)) {
|
||||
EXIT("cannot scale host clock, ticks=0x%016" PRIx64 " frequency=%" PRIu64 "\n",
|
||||
host_ticks, host_frequency);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void SubmitLabel32(CommandBuffer* buffer, uint32_t* dst, uint32_t value,
|
||||
LabelCallback callback_1 = nullptr, LabelCallback callback_2 = nullptr,
|
||||
@@ -158,7 +194,7 @@ void GraphicsRenderWriteAtEndOfPipeClockCounter(uint64_t submit_id, CommandBuffe
|
||||
buffer->SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopWrite), submit_id, 8, 0, 0,
|
||||
0, reinterpret_cast<uint64_t>(dst_gpu_addr));
|
||||
|
||||
const auto value = LibKernel::KernelReadTsc();
|
||||
const auto value = GraphicsRenderReadReferenceClock();
|
||||
PublishImmediateFence(dst_gpu_addr, value);
|
||||
LOGF_COLOR(Log::Color::BrightGreen,
|
||||
"EndOfPipe Signal!!! [0x%016" PRIx64 "] <- Clock: 0x%016" PRIx64 "\n",
|
||||
@@ -176,7 +212,7 @@ void GraphicsRenderWriteAtEndOfPipeClockCounterWithWriteBack(uint64_t subm
|
||||
buffer->SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopWriteBack), submit_id, 8, 0,
|
||||
0, 0, reinterpret_cast<uint64_t>(dst_gpu_addr));
|
||||
|
||||
const auto value = LibKernel::KernelReadTsc();
|
||||
const auto value = GraphicsRenderReadReferenceClock();
|
||||
PublishImmediateFence(dst_gpu_addr, value);
|
||||
LOGF_COLOR(Log::Color::BrightGreen,
|
||||
"EndOfPipe Signal!!! [0x%016" PRIx64 "] <- Clock: 0x%016" PRIx64 "\n",
|
||||
|
||||
@@ -591,7 +591,8 @@ bool Equal(const DepthTargetInfo& left, const DepthTargetInfo& right) {
|
||||
left.htile_size == right.htile_size && left.format == right.format &&
|
||||
left.guest_format == right.guest_format && left.width == right.width &&
|
||||
left.height == right.height && left.pitch == right.pitch &&
|
||||
left.bytes_per_element == right.bytes_per_element && left.tile_mode == right.tile_mode;
|
||||
left.bytes_per_element == right.bytes_per_element && left.tile_mode == right.tile_mode &&
|
||||
left.stencil_htile_compressed == right.stencil_htile_compressed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCoherentGuestImageSource(const BufferImageCopySource& source,
|
||||
@@ -863,6 +864,11 @@ void CreateDepthViews(GraphicContext* ctx, DepthStencilVulkanImage* image) {
|
||||
{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO,
|
||||
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO},
|
||||
0, 0, 1, 1);
|
||||
UtilCreateImageView(ctx, image, VulkanImage::VIEW_R001, VK_IMAGE_VIEW_TYPE_2D,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO,
|
||||
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE},
|
||||
0, 0, 1, 1);
|
||||
}
|
||||
|
||||
DepthStencilVulkanImage* CreateDepthTarget(GraphicContext* ctx, const DepthTargetInfo& info,
|
||||
@@ -1840,6 +1846,7 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G
|
||||
PageOverlaps(info.address, info.size, info.htile_address, info.htile_size) ||
|
||||
(has_stencil && PageOverlaps(info.stencil_address, info.stencil_size,
|
||||
info.htile_address, info.htile_size)))) ||
|
||||
(info.stencil_htile_compressed && (!has_stencil || !has_htile)) ||
|
||||
!(info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm)
|
||||
? !has_stencil && info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2
|
||||
: info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) &&
|
||||
@@ -1871,14 +1878,23 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G
|
||||
" elements=0x%016" PRIx64 "\n",
|
||||
info.stencil_size, elements);
|
||||
}
|
||||
if (has_stencil && !info.stencil_load_clear && !CanLoadRawStencilPlane(info)) {
|
||||
EXIT("TextureCache: HTile-compressed stencil load is unsupported, addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 " htile=0x%016" PRIx64 "+0x%016" PRIx64 "\n",
|
||||
info.stencil_address, info.stencil_size, info.htile_address, info.htile_size);
|
||||
}
|
||||
if (has_htile) {
|
||||
RegisterMeta(info.htile_address, info.htile_size);
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
const auto depth_source = m_buffer_cache.ObtainBufferForImage(info.address, info.size);
|
||||
if (has_stencil) {
|
||||
(void)m_buffer_cache.ObtainBufferForImage(info.stencil_address, info.stencil_size);
|
||||
}
|
||||
// BufferCache treats an untracked guest range as CPU-current. That is useful when
|
||||
// uploading from guest memory, but it is not evidence that a clean native image is
|
||||
// stale. Only a genuinely overlapping buffer participates in transition selection.
|
||||
const bool depth_buffer_overlap = m_buffer_cache.HasPageOverlap(info.address, info.size);
|
||||
const auto depth_source = m_buffer_cache.ObtainBufferForImage(info.address, info.size);
|
||||
const auto stencil_source =
|
||||
has_stencil ? m_buffer_cache.ObtainBufferForImage(info.stencil_address, info.stencil_size)
|
||||
: BufferImageCopySource {};
|
||||
FaultSafeTextureLock lock(this, m_lock);
|
||||
RequireNoMetaOverlapLocked(info.address, info.size);
|
||||
if (has_stencil) {
|
||||
@@ -1900,37 +1916,32 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G
|
||||
if (has_stencil && info.stencil_load_clear) {
|
||||
match->stencil_initialized = true;
|
||||
}
|
||||
if (!match->gpu_modified && m_memory_tracker.IsRegionCpuModified(info.address, info.size)) {
|
||||
if (has_stencil && match->stencil_initialized && !info.stencil_load_clear) {
|
||||
EXIT("TextureCache: depth refresh would discard initialized stencil, "
|
||||
"addr=0x%016" PRIx64 "\n",
|
||||
info.address);
|
||||
}
|
||||
if (!match->gpu_modified && depth_source.cpu_dirty) {
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
info.address, info.size, false, [](uint64_t, uint64_t) noexcept {},
|
||||
[&]() noexcept {
|
||||
m_tiler.DetileImage(ctx,
|
||||
static_cast<DepthStencilVulkanImage*>(match->image),
|
||||
match->depth, depth_source, true);
|
||||
static_cast<DepthStencilVulkanImage*>(match->image),
|
||||
match->depth, depth_source, true);
|
||||
});
|
||||
}
|
||||
if (!match->gpu_modified && has_stencil &&
|
||||
m_memory_tracker.IsRegionCpuModified(info.stencil_address, info.stencil_size)) {
|
||||
if (!info.stencil_load_clear) {
|
||||
if (info.stencil_access) {
|
||||
EXIT("TextureCache: stencil refresh from guest memory is unsupported, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
info.stencil_address, info.stencil_size);
|
||||
}
|
||||
} else {
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
info.stencil_address, info.stencil_size, false,
|
||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||
}
|
||||
if (!match->gpu_modified && has_stencil && stencil_source.cpu_dirty) {
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
info.stencil_address, info.stencil_size, false,
|
||||
[](uint64_t, uint64_t) noexcept {},
|
||||
[&]() noexcept {
|
||||
if (!info.stencil_load_clear) {
|
||||
m_tiler.DetileStencil(
|
||||
ctx, static_cast<DepthStencilVulkanImage*>(match->image), match->depth,
|
||||
stencil_source, true);
|
||||
match->stencil_initialized = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return static_cast<DepthStencilVulkanImage*>(match->image);
|
||||
}
|
||||
std::vector<CachedImage*> retire;
|
||||
std::shared_ptr<CachedImage> sampled_depth_source;
|
||||
for (const auto& entry: m_images) {
|
||||
auto& cached = *entry;
|
||||
const bool overlaps =
|
||||
@@ -1947,6 +1958,9 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G
|
||||
info.address, info.size, static_cast<uint32_t>(cached.kind), cached.Address(),
|
||||
cached.Size());
|
||||
}
|
||||
if (!info.depth_load_clear && sampled_depth_source == nullptr) {
|
||||
sampled_depth_source = entry;
|
||||
}
|
||||
retire.push_back(&cached);
|
||||
}
|
||||
std::vector<ImageRetirementRange> ranges;
|
||||
@@ -1979,19 +1993,64 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G
|
||||
cached->ctx = ctx;
|
||||
cached->stencil_initialized = !has_stencil || info.stencil_load_clear;
|
||||
cached->image = CreateDepthTarget(ctx, info, &cached->memory);
|
||||
if (sampled_depth_source != nullptr &&
|
||||
(sampled_depth_source->image->type != VulkanImageType::Texture ||
|
||||
sampled_depth_source->image->format != Prospero::SurfaceFormat(info.guest_format) ||
|
||||
sampled_depth_source->image->extent.width != info.width ||
|
||||
sampled_depth_source->image->extent.height != info.height)) {
|
||||
EXIT("TextureCache: sampled-depth native source is inconsistent, format=%d/%d "
|
||||
"extent=%ux%u/%ux%u type=%u\n",
|
||||
static_cast<int>(sampled_depth_source->image->format),
|
||||
static_cast<int>(Prospero::SurfaceFormat(info.guest_format)),
|
||||
sampled_depth_source->image->extent.width,
|
||||
sampled_depth_source->image->extent.height, info.width, info.height,
|
||||
static_cast<uint32_t>(sampled_depth_source->image->type));
|
||||
}
|
||||
const auto transition_source = SelectDepthTransitionSource(
|
||||
info.depth_load_clear, sampled_depth_source != nullptr,
|
||||
sampled_depth_source != nullptr && sampled_depth_source->info.IsCpuDirty(),
|
||||
sampled_depth_source != nullptr && sampled_depth_source->buffer_modified,
|
||||
depth_buffer_overlap, depth_source.cpu_dirty);
|
||||
LOGF("TextureCache: depth transition source=%u sampled=%d sampled_cpu_dirty=%d "
|
||||
"sampled_buffer_dirty=%d buffer_overlap=%d buffer_cpu_dirty=%d addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 "\n",
|
||||
static_cast<uint32_t>(transition_source), sampled_depth_source != nullptr,
|
||||
sampled_depth_source != nullptr && sampled_depth_source->info.IsCpuDirty(),
|
||||
sampled_depth_source != nullptr && sampled_depth_source->buffer_modified,
|
||||
depth_buffer_overlap, depth_source.cpu_dirty, info.address, info.size);
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
info.address, info.size, false, [](uint64_t, uint64_t) noexcept {},
|
||||
[&]() noexcept {
|
||||
if (!info.depth_load_clear) {
|
||||
m_tiler.DetileImage(ctx,
|
||||
static_cast<DepthStencilVulkanImage*>(cached->image),
|
||||
info, depth_source, false);
|
||||
switch (transition_source) {
|
||||
case DepthTransitionSource::None: break;
|
||||
case DepthTransitionSource::Guest:
|
||||
m_tiler.DetileImage(ctx, static_cast<DepthStencilVulkanImage*>(cached->image),
|
||||
info, depth_source, false);
|
||||
break;
|
||||
case DepthTransitionSource::Native:
|
||||
command->RetainResourceUntilFence(sampled_depth_source);
|
||||
UtilCopyImageWithBuffer(
|
||||
command, ctx, sampled_depth_source->image, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
cached->image, VK_IMAGE_ASPECT_DEPTH_BIT, info.bytes_per_element,
|
||||
static_cast<uint64_t>(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL));
|
||||
LOGF("TextureCache: preserved sampled depth through native buffer copy, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
info.address, info.size);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (has_stencil) {
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
info.stencil_address, info.stencil_size, false, [](uint64_t, uint64_t) noexcept {},
|
||||
[]() noexcept {});
|
||||
info.stencil_address, info.stencil_size, false,
|
||||
[](uint64_t, uint64_t) noexcept {},
|
||||
[&]() noexcept {
|
||||
if (!info.stencil_load_clear) {
|
||||
m_tiler.DetileStencil(
|
||||
ctx, static_cast<DepthStencilVulkanImage*>(cached->image), info,
|
||||
stencil_source, false);
|
||||
}
|
||||
});
|
||||
cached->stencil_initialized = true;
|
||||
}
|
||||
auto* image = static_cast<DepthStencilVulkanImage*>(cached->image);
|
||||
m_images.push_back(std::move(cached));
|
||||
|
||||
@@ -50,8 +50,8 @@ void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image,
|
||||
}
|
||||
|
||||
void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
|
||||
const DepthTargetInfo& info, const BufferImageCopySource& source,
|
||||
bool refresh) const {
|
||||
const DepthTargetInfo& info, const BufferImageCopySource& source,
|
||||
bool refresh) const {
|
||||
const bool d16 = info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) &&
|
||||
info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2;
|
||||
const bool d32 =
|
||||
@@ -80,7 +80,54 @@ void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
|
||||
TileConvertTiledToLinearDepth(linear.Data(), reinterpret_cast<const void*>(source.address),
|
||||
info.guest_format, info.width, info.height, info.pitch,
|
||||
info.size);
|
||||
UtilFillDepthImage(ctx, image, linear.Data(), info.size, info.pitch);
|
||||
UtilFillImage(ctx, image, linear.Data(), info.size, info.pitch, VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
}
|
||||
|
||||
void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
|
||||
const DepthTargetInfo& info, const BufferImageCopySource& source,
|
||||
bool refresh) const {
|
||||
TileSizeAlign stencil_size {};
|
||||
TileSizeAlign htile_size {};
|
||||
TileSizeAlign depth_size {};
|
||||
const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
|
||||
const auto stencil_pitch = TileGetTexturePitch(
|
||||
stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
|
||||
const bool supported_layout =
|
||||
TileGetDepthSize(info.width, info.height, 0,
|
||||
Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F),
|
||||
Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt),
|
||||
info.htile_address != 0, &stencil_size, &htile_size, &depth_size) &&
|
||||
stencil_size.size == info.stencil_size && stencil_size.align == 65536 &&
|
||||
stencil_pitch != 0;
|
||||
if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 ||
|
||||
info.stencil_address == 0 || info.stencil_size == 0 || info.width == 0 ||
|
||||
info.height == 0 || info.format != VK_FORMAT_D32_SFLOAT_S8_UINT ||
|
||||
info.guest_format != Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) ||
|
||||
info.bytes_per_element != 4 ||
|
||||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) ||
|
||||
info.stencil_htile_compressed || !supported_layout || !source.cpu_current ||
|
||||
source.address != info.stencil_address ||
|
||||
source.size != info.stencil_size || (source.buffer == nullptr && source.offset != 0)) {
|
||||
EXIT("Tiler: unsupported stencil detile, ctx=%p image=%p source_buffer=%p "
|
||||
"source=0x%016" PRIx64 "+0x%016" PRIx64 " offset=0x%016" PRIx64
|
||||
" current=%d stencil=0x%016" PRIx64 "+0x%016" PRIx64
|
||||
" extent=%ux%u depth_pitch=%u stencil_pitch=%u tile=%u format=%d guest_format=%u "
|
||||
"bpe=%u\n",
|
||||
static_cast<const void*>(ctx), static_cast<const void*>(image),
|
||||
static_cast<const void*>(source.buffer), source.address, source.size, source.offset,
|
||||
source.cpu_current, info.stencil_address, info.stencil_size, info.width, info.height,
|
||||
info.pitch, stencil_pitch, info.tile_mode, static_cast<int>(info.format),
|
||||
info.guest_format, info.bytes_per_element);
|
||||
}
|
||||
if (refresh) {
|
||||
VulkanDeviceWaitIdle(ctx);
|
||||
}
|
||||
UtilScratchBuffer linear(info.stencil_size);
|
||||
TileConvertTiledToLinearDepth(linear.Data(), reinterpret_cast<const void*>(source.address),
|
||||
stencil_format, info.width, info.height, stencil_pitch,
|
||||
info.stencil_size);
|
||||
UtilFillImage(ctx, image, linear.Data(), info.stencil_size, stencil_pitch,
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||
}
|
||||
|
||||
void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info) const {
|
||||
|
||||
@@ -21,6 +21,9 @@ public:
|
||||
void DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
|
||||
const DepthTargetInfo& info, const BufferImageCopySource& source,
|
||||
bool refresh) const;
|
||||
void DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
|
||||
const DepthTargetInfo& info, const BufferImageCopySource& source,
|
||||
bool refresh) const;
|
||||
void TileImage(void* dst, const void* src, const RenderTargetInfo& info) const;
|
||||
void TileImage(void* dst, const void* src, const DepthTargetInfo& info) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user