mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
graphics: represent rebased contiguous mip-tail texture views
Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
@@ -21,6 +21,54 @@ bool IsDepthPlane(const ImageInfo& info) {
|
||||
info.tile_mode == Prospero::TileMode::kLinear);
|
||||
}
|
||||
|
||||
GuestRange ContiguousMipRange(const ImageInfo& info, uint32_t level) {
|
||||
const auto& block = info.tiling.block;
|
||||
if (info.tile_mode != Prospero::TileMode::kStandard4KB ||
|
||||
block.family != TileBlockFamily::Standard4KB || info.IsVolume() || info.samples != 1 ||
|
||||
info.HasMetadata() || level >= info.resources.levels || level >= info.mip_layout.size() ||
|
||||
info.tiling.texel_width == 0 || info.tiling.texel_height == 0) {
|
||||
return {};
|
||||
}
|
||||
const auto extent = info.MipExtent(level);
|
||||
const auto width = (extent.width + info.tiling.texel_width - 1u) / info.tiling.texel_width;
|
||||
const auto height = (extent.height + info.tiling.texel_height - 1u) / info.tiling.texel_height;
|
||||
const auto& mip = info.mip_layout[level];
|
||||
uint32_t last = 0, origin = 0;
|
||||
if (!std::has_single_bit(width) || !std::has_single_bit(height) ||
|
||||
width > block.block_width || height > block.block_height ||
|
||||
mip.tail_x > block.block_width - width || mip.tail_y > block.block_height - height ||
|
||||
mip.tail_x % width != 0 || mip.tail_y % height != 0 ||
|
||||
!TileGetBlockOffset(block, width - 1u, height - 1u, 0, last) ||
|
||||
!TileGetBlockOffset(block, mip.tail_x, mip.tail_y, 0, origin)) {
|
||||
return {};
|
||||
}
|
||||
// Standard4KB interleaves monotonically ordered X/Y bits. A dense rectangle
|
||||
// fills exactly this power-of-two interval, including when rebased into a tail.
|
||||
const uint64_t size = uint64_t {last} + block.bytes_per_element;
|
||||
if (!std::has_single_bit(size) || size != uint64_t {width} * height * block.bytes_per_element ||
|
||||
(origin & (size - 1u)) != 0 || size > block.block_size - origin ||
|
||||
mip.offset > UINT64_MAX - origin) {
|
||||
return {};
|
||||
}
|
||||
return {mip.offset + origin, size};
|
||||
}
|
||||
|
||||
uint32_t CompactMipSize(const ImageInfo& info) {
|
||||
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
||||
return 0;
|
||||
}
|
||||
const auto range = ContiguousMipRange(info, 0);
|
||||
if (range.size == 0 || range.address != 0) {
|
||||
return 0;
|
||||
}
|
||||
const auto alignment = std::max<uint64_t>(256, range.size);
|
||||
const auto in_block = info.data.address & (info.tiling.block.block_size - 1u);
|
||||
return (info.data.address & (alignment - 1u)) == 0 &&
|
||||
range.size <= info.tiling.block.block_size - in_block
|
||||
? static_cast<uint32_t>(range.size)
|
||||
: 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::Extent3D ImageInfo::MipExtent(uint32_t level) const {
|
||||
@@ -34,6 +82,7 @@ void ImageInfo::UpdateSize() {
|
||||
extent.height == 0 || extent.depth == 0 || resources.levels == 0 ||
|
||||
resources.levels > mip_layout.size() || resources.layers == 0 || samples == 0 ||
|
||||
!std::has_single_bit(samples));
|
||||
const bool auto_size = data.size == 0;
|
||||
TileTextureElementLayout element {};
|
||||
EXIT_NOT_IMPLEMENTED(!TileGetTextureElementLayout(guest_format, element));
|
||||
if (bytes_per_block == 0) {
|
||||
@@ -164,9 +213,86 @@ void ImageInfo::UpdateSize() {
|
||||
linear_offset = Add(mip.linear_offset, Multiply(mip.linear_size, active.depth));
|
||||
mip_pitch = std::max(mip_pitch >> 1u, 1u);
|
||||
}
|
||||
const auto compact_size = CompactMipSize(*this);
|
||||
if (compact_size != 0 && (auto_size || data.size == compact_size)) {
|
||||
data.size = mip_layout[0].size = compact_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ImageInfo::DataAlignment() const {
|
||||
if (!IsTiled()) {
|
||||
return 256;
|
||||
}
|
||||
const auto compact_size = CompactMipSize(*this);
|
||||
if (compact_size != 0 && data.size == compact_size && mip_layout[0].size == compact_size) {
|
||||
return std::max(256u, compact_size);
|
||||
}
|
||||
return tiling.block.block_size;
|
||||
}
|
||||
|
||||
int32_t ImageInfo::SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || type != container.type ||
|
||||
IsVolume() || resources.levels != 1 || resources.layers == 0 ||
|
||||
container.resources.layers == 0 || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
!data.Valid() || !container.data.Valid() || data.address < container.data.address ||
|
||||
data.End() > container.data.End()) {
|
||||
return -1;
|
||||
}
|
||||
if (HasStencil() != container.HasStencil() ||
|
||||
(HasStencil() && (stencil.address < container.stencil.address ||
|
||||
stencil.End() > container.stencil.End()))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u) ||
|
||||
extent.depth != container.extent.depth || mip_layout[0].pitch != layout.pitch ||
|
||||
mip_layout[0].height != layout.height || layout.size == 0 ||
|
||||
layout.size % container.resources.layers != 0 ||
|
||||
container.data.size % container.resources.layers != 0 ||
|
||||
data.size % resources.layers != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// PS5 block slices contain the entire reversed mip chain. A rebased tail
|
||||
// descriptor covers only its active bytes within the shared tail block.
|
||||
const auto slice_stride = container.data.size / container.resources.layers;
|
||||
const auto child_stride = data.size / resources.layers;
|
||||
auto mip_offset = layout.offset;
|
||||
auto mip_size = layout.size / container.resources.layers;
|
||||
const auto compact_size = CompactMipSize(*this);
|
||||
if (compact_size != 0 && child_stride == compact_size && mip_layout[0].size == compact_size) {
|
||||
const auto range = ContiguousMipRange(container, level);
|
||||
if (range.size != compact_size) {
|
||||
return -1;
|
||||
}
|
||||
mip_offset = range.address;
|
||||
mip_size = range.size;
|
||||
} else if (mip_layout[0].tail_x != layout.tail_x || mip_layout[0].tail_y != layout.tail_y) {
|
||||
return -1;
|
||||
}
|
||||
if (child_stride != mip_size || mip_offset >= slice_stride ||
|
||||
child_stride > slice_stride - mip_offset ||
|
||||
(resources.layers > 1 && child_stride != slice_stride)) {
|
||||
return -1;
|
||||
}
|
||||
const auto address_delta = data.address - container.data.address;
|
||||
if (address_delta < mip_offset || (address_delta - mip_offset) % slice_stride != 0) {
|
||||
return -1;
|
||||
}
|
||||
const auto layer = (address_delta - mip_offset) / slice_stride;
|
||||
if (layer > INT32_MAX || layer >= container.resources.layers ||
|
||||
resources.layers > container.resources.layers - layer) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int32_t>(layer);
|
||||
}
|
||||
|
||||
uint64_t ImageInfo::LinearSize(uint32_t levels) const {
|
||||
levels = levels == 0 ? resources.levels : levels;
|
||||
EXIT_IF(levels == 0 || levels > resources.levels || levels > mip_layout.size());
|
||||
|
||||
@@ -88,6 +88,7 @@ struct ImageInfo {
|
||||
uint32_t first_tail_level = 16;
|
||||
|
||||
void UpdateSize();
|
||||
[[nodiscard]] uint32_t DataAlignment() const;
|
||||
[[nodiscard]] uint64_t LinearSize(uint32_t levels = 0) const;
|
||||
[[nodiscard]] vk::Extent3D MipExtent(uint32_t level) const;
|
||||
[[nodiscard]] std::vector<vk::BufferImageCopy> BufferCopies(uint64_t buffer_offset = 0,
|
||||
@@ -136,53 +137,7 @@ struct ImageInfo {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || type != container.type ||
|
||||
IsVolume() || resources.levels != 1 || resources.layers == 0 ||
|
||||
container.resources.layers == 0 || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
!data.Valid() || !container.data.Valid() || data.address < container.data.address ||
|
||||
data.End() > container.data.End()) {
|
||||
return -1;
|
||||
}
|
||||
if (HasStencil() != container.HasStencil() ||
|
||||
(HasStencil() && (stencil.address < container.stencil.address ||
|
||||
stencil.End() > container.stencil.End()))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u) ||
|
||||
extent.depth != container.extent.depth || mip_layout[0].pitch != layout.pitch ||
|
||||
mip_layout[0].height != layout.height || layout.size == 0 ||
|
||||
layout.size % container.resources.layers != 0 ||
|
||||
container.data.size % container.resources.layers != 0 ||
|
||||
data.size % resources.layers != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// PS5 block slices contain the entire reversed mip chain.
|
||||
const auto slice_stride = container.data.size / container.resources.layers;
|
||||
const auto child_stride = data.size / resources.layers;
|
||||
if (child_stride != layout.size / container.resources.layers ||
|
||||
layout.offset >= slice_stride || child_stride > slice_stride - layout.offset ||
|
||||
(resources.layers > 1 && child_stride != slice_stride)) {
|
||||
return -1;
|
||||
}
|
||||
const auto address_delta = data.address - container.data.address;
|
||||
if (address_delta < layout.offset || (address_delta - layout.offset) % slice_stride != 0) {
|
||||
return -1;
|
||||
}
|
||||
const auto layer = (address_delta - layout.offset) / slice_stride;
|
||||
if (layer > INT32_MAX || layer >= container.resources.layers ||
|
||||
resources.layers > container.resources.layers - layer) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int32_t>(layer);
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept;
|
||||
};
|
||||
|
||||
struct ImageViewInfo {
|
||||
|
||||
@@ -608,6 +608,9 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
block_bytes != 0 ? block_bytes : Prospero::NumBytesPerElement(format);
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = tile;
|
||||
if (!resource.r128 && descriptor.MetaCompress() && tile != Prospero::TileMode::kDepth) {
|
||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||
}
|
||||
if (levels > physical_levels) {
|
||||
if (!ResolveTextureMipView(desc.info, physical_levels,
|
||||
!resource.r128 && descriptor.MetaCompress(), view_levels,
|
||||
@@ -620,8 +623,7 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
} else {
|
||||
desc.info.UpdateSize();
|
||||
}
|
||||
const auto alignment =
|
||||
tile == Prospero::TileMode::kLinear ? 256u : desc.info.tiling.block.block_size;
|
||||
const auto alignment = desc.info.DataAlignment();
|
||||
EXIT_NOT_IMPLEMENTED(desc.info.data.size == 0 || alignment == 0 ||
|
||||
(address & (static_cast<uint64_t>(alignment) - 1u)) != 0);
|
||||
if (storage) {
|
||||
@@ -632,7 +634,6 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
(void)TileGetDccSize(width, height, volume ? depth : image_layers,
|
||||
desc.info.bytes_per_block, physical_levels, tile, metadata_size,
|
||||
std::countr_zero(samples));
|
||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||
desc.info.metadata.range = {descriptor.MetaAddr() << 8u, metadata_size.size};
|
||||
desc.info.metadata.dcc_alpha_msb = descriptor.DccAlphaPos();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user