mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3da05eff71 | |||
| 489d483362 |
@@ -8,9 +8,8 @@
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
@@ -57,32 +56,11 @@ HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host.format, bytes, host.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume_texture) {
|
||||
size_t GetTextureRegionCount(uint32_t depth, uint32_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
@@ -90,63 +68,15 @@ static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume
|
||||
return count;
|
||||
}
|
||||
|
||||
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s,
|
||||
Prospero::ColorComponentMapping storage_to_host) {
|
||||
if (s >= 4u && s <= 7u) {
|
||||
s = static_cast<uint8_t>(4u + storage_to_host.Map(s - 4u));
|
||||
}
|
||||
switch (static_cast<Prospero::CompSwizzle>(s)) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return vk::ComponentSwizzle::eR;
|
||||
case Prospero::CompSwizzle::kGreen: return vk::ComponentSwizzle::eG;
|
||||
case Prospero::CompSwizzle::kBlue: return vk::ComponentSwizzle::eB;
|
||||
case Prospero::CompSwizzle::kAlpha: return vk::ComponentSwizzle::eA;
|
||||
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage) {
|
||||
Prospero::ColorComponentMapping storage_to_host {0u};
|
||||
for (uint32_t host = 0; host < 4u; ++host) {
|
||||
storage_to_host.packed |= static_cast<uint8_t>(host << (host_to_storage.Map(host) * 2u));
|
||||
}
|
||||
vk::ComponentMapping components {};
|
||||
components.r = TextureGetComponentSwizzle(GetDstSel(swizzle, 0), storage_to_host);
|
||||
components.g = TextureGetComponentSwizzle(GetDstSel(swizzle, 1), storage_to_host);
|
||||
components.b = TextureGetComponentSwizzle(GetDstSel(swizzle, 2), storage_to_host);
|
||||
components.a = TextureGetComponentSwizzle(GetDstSel(swizzle, 3), storage_to_host);
|
||||
return components;
|
||||
}
|
||||
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto host = ResolveHostFormat(backing_format, Prospero::ChannelOrder::kStandard);
|
||||
const auto conversion_format =
|
||||
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||
if (host.format != vk::Format::eUndefined) {
|
||||
return {host.format, conversion_format, host.host_to_storage};
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t levels,
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint32_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
stride = std::max(stride, mips[i].offset + mips[i].size);
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
stride = std::max(stride, mips[mip_level].offset + mips[mip_level].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
const auto guest_stride = total_size / depth;
|
||||
if (guest_stride >= stride) {
|
||||
stride = guest_stride;
|
||||
}
|
||||
stride = std::max(stride, total_size / depth);
|
||||
}
|
||||
|
||||
return stride;
|
||||
@@ -154,48 +84,102 @@ uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t lev
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t elements_w =
|
||||
const uint32_t element_columns =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t elements_h =
|
||||
const uint32_t element_rows =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(elements_w) * elements_h * element.bytes;
|
||||
return static_cast<uint64_t>(element_columns) * element_rows * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint64_t height,
|
||||
uint64_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto pitch = base_pitch;
|
||||
auto h = static_cast<uint32_t>(height);
|
||||
const TileTextureElementLayout& element, uint32_t height,
|
||||
uint32_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto mip_pitch = base_pitch;
|
||||
auto mip_height = height;
|
||||
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, pitch, h);
|
||||
mips[i].size = size;
|
||||
mips[i].offset = offset;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, mip_pitch, mip_height);
|
||||
mips[mip_level] = {offset, size};
|
||||
|
||||
offset += size;
|
||||
if (pitch > 1) {
|
||||
pitch /= 2;
|
||||
if (mip_pitch > 1) {
|
||||
mip_pitch /= 2;
|
||||
}
|
||||
if (h > 1) {
|
||||
h /= 2;
|
||||
if (mip_height > 1) {
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool FitsBufferRange(uint64_t offset, uint64_t size, uint64_t capacity) {
|
||||
return offset <= capacity && size <= capacity - offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host_format = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host_format.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage) {
|
||||
constexpr std::array host_components {vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG,
|
||||
vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA};
|
||||
std::array<vk::ComponentSwizzle, 4> storage_components {};
|
||||
for (uint32_t host_component = 0; host_component < host_components.size(); ++host_component) {
|
||||
storage_components[host_to_storage.Map(host_component)] = host_components[host_component];
|
||||
}
|
||||
const auto resolve = [&](uint32_t component) {
|
||||
const auto selector = static_cast<Prospero::CompSwizzle>(GetDstSel(swizzle, component));
|
||||
switch (selector) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return storage_components[0];
|
||||
case Prospero::CompSwizzle::kGreen: return storage_components[1];
|
||||
case Prospero::CompSwizzle::kBlue: return storage_components[2];
|
||||
case Prospero::CompSwizzle::kAlpha: return storage_components[3];
|
||||
default: EXIT("unknown swizzle: %u\n", static_cast<uint32_t>(selector));
|
||||
}
|
||||
};
|
||||
return {resolve(0), resolve(1), resolve(2), resolve(3)};
|
||||
}
|
||||
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto host_format = ResolveHostFormat(backing_format, Prospero::ChannelOrder::kStandard);
|
||||
const auto conversion_format =
|
||||
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||
if (host_format.format != vk::Format::eUndefined) {
|
||||
return {host_format.format, conversion_format, host_format.host_to_storage};
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
}
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner) {
|
||||
TextureUploadLayout layout {};
|
||||
layout.surface.description = {
|
||||
format,
|
||||
tile,
|
||||
tile_mode,
|
||||
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
|
||||
width,
|
||||
height,
|
||||
@@ -204,7 +188,6 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
volume_texture ? 1u : depth,
|
||||
};
|
||||
const auto& description = layout.surface.description;
|
||||
const auto tile_mode = description.tile_mode;
|
||||
TileTextureElementLayout element {};
|
||||
|
||||
if (format == Prospero::BufferFormat::kInvalid) {
|
||||
@@ -214,34 +197,19 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
levels);
|
||||
}
|
||||
|
||||
switch (tile_mode) {
|
||||
case Prospero::TileMode::kLinear:
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
if (!allow_depth_tile || !TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile_mode != Prospero::TileMode::kLinear) {
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
} else {
|
||||
if ((tile_mode == Prospero::TileMode::kDepth && !allow_depth_tile) ||
|
||||
!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format), static_cast<uint32_t>(tile_mode),
|
||||
upload_size, width, height, levels);
|
||||
}
|
||||
element = {layout.surface.texture.block.bytes_per_element,
|
||||
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
|
||||
}
|
||||
@@ -274,40 +242,35 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const bool linear_texture = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
uint32_t mip_width = description.width;
|
||||
uint32_t mip_height = description.height;
|
||||
uint32_t mip_pitch = volume_texture && description.tile_mode != Prospero::TileMode::kLinear
|
||||
? description.width
|
||||
: layout.pitch;
|
||||
uint32_t mip_pitch = volume_texture && !linear_texture ? description.width : layout.pitch;
|
||||
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
regions.reserve(GetTextureRegionCount(depth, description.levels, volume_texture));
|
||||
for (uint32_t i = 0; i < description.levels; i++) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[i].size == 0);
|
||||
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, i, volume_texture);
|
||||
|
||||
for (uint32_t z = 0; z < mip_depth; z++) {
|
||||
const auto slice_offset = z * layout.slice_stride;
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = layout.mips[i].offset + slice_offset;
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, volume_texture ? 0u : z,
|
||||
1};
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
const bool linear = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
if (linear) {
|
||||
region.bufferRowLength = layout.mips[i].row_length;
|
||||
region.bufferImageHeight = layout.mips[i].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align = [](uint32_t value, uint32_t block) {
|
||||
return ((value + block - 1u) / block) * block;
|
||||
};
|
||||
const auto pitch = align(mip_pitch, texel_width);
|
||||
region.bufferRowLength = pitch > align(mip_width, texel_width) ? pitch : 0;
|
||||
}
|
||||
for (uint32_t mip_level = 0; mip_level < description.levels; ++mip_level) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[mip_level].size == 0);
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
vk::BufferImageCopy region {};
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, mip_level, 0, 1};
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
if (linear_texture) {
|
||||
region.bufferRowLength = layout.mips[mip_level].row_length;
|
||||
region.bufferImageHeight = layout.mips[mip_level].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align_to_texel = [texel_width](uint32_t dimension) {
|
||||
return ((dimension + texel_width - 1u) / texel_width) * texel_width;
|
||||
};
|
||||
const auto aligned_pitch = align_to_texel(mip_pitch);
|
||||
region.bufferRowLength = aligned_pitch > align_to_texel(mip_width) ? aligned_pitch : 0;
|
||||
}
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; ++slice_index) {
|
||||
region.bufferOffset = layout.mips[mip_level].offset + slice_index * layout.slice_stride;
|
||||
region.imageSubresource.baseArrayLayer = volume_texture ? 0u : slice_index;
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(slice_index) : 0;
|
||||
regions.push_back(region);
|
||||
}
|
||||
|
||||
@@ -325,17 +288,9 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
return regions;
|
||||
}
|
||||
|
||||
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t& size) {
|
||||
if (offset > capacity || length > capacity - offset) {
|
||||
return false;
|
||||
}
|
||||
size = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& out_infos) {
|
||||
std::vector<GpuTileInfo>& out_tile_infos) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
@@ -353,111 +308,69 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GpuTileInfo> infos;
|
||||
infos.reserve(regions.size());
|
||||
if (volume_texture) {
|
||||
size_t region_base = 0;
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
const uint32_t mip_depth = GetTextureLevelDepth(depth, level, true);
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const uint64_t linear_stride = layout.slice_stride;
|
||||
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
||||
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
||||
const auto& region = regions[region_base + z];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset =
|
||||
static_cast<uint64_t>(z / block.block_depth) * surface.block_slice_size +
|
||||
surface.mips[level].offset;
|
||||
const uint64_t linear_span =
|
||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||
layout.mips[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, surface.mips[level].size, tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.linear_slice_stride = linear_stride;
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.depth = copy_depth;
|
||||
info.surface_z =
|
||||
std::vector<GpuTileInfo> tile_infos;
|
||||
tile_infos.reserve(regions.size());
|
||||
const auto slices_per_copy = volume_texture ? block.block_depth : 1u;
|
||||
size_t region_base = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto& mip = surface.mips[mip_level];
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; slice_index += slices_per_copy) {
|
||||
const auto& region = regions[region_base + slice_index];
|
||||
GpuTileInfo tile_info {};
|
||||
tile_info.family = block.family;
|
||||
tile_info.bytes_per_element = block.bytes_per_element;
|
||||
tile_info.linear_offset = region.bufferOffset;
|
||||
tile_info.linear_size = layout.mips[mip_level].size;
|
||||
tile_info.tiled_size = mip.size;
|
||||
if (volume_texture) {
|
||||
tile_info.depth = std::min(slices_per_copy, mip_depth - slice_index);
|
||||
tile_info.linear_slice_stride = layout.slice_stride;
|
||||
tile_info.linear_size += (tile_info.depth - 1u) * layout.slice_stride;
|
||||
tile_info.tiled_offset =
|
||||
mip.offset +
|
||||
static_cast<uint64_t>(slice_index / slices_per_copy) * surface.block_slice_size;
|
||||
tile_info.surface_z =
|
||||
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail_x = tail ? surface.mips[level].tail_x : 0;
|
||||
info.tail_y = tail ? surface.mips[level].tail_y : 0;
|
||||
info.tail = tail;
|
||||
info.tiled_width = surface.mips[level].padded_width;
|
||||
info.tiled_height = surface.mips[level].padded_height;
|
||||
infos.push_back(info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
} else {
|
||||
size_t region_index = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
const auto& level_size = layout.mips[level];
|
||||
const auto& mip = surface.mips[level];
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const auto level_depth = GetTextureLevelDepth(depth, level, false);
|
||||
for (uint32_t z = 0; z < level_depth; z++) {
|
||||
const auto& region = regions[region_index++];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
} else {
|
||||
const auto source_stride = layout.source_slice_stride != 0
|
||||
? layout.source_slice_stride
|
||||
: surface.block_slice_size;
|
||||
if (z > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
if (slice_index > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
return false;
|
||||
}
|
||||
info.tiled_offset = mip.offset + static_cast<uint64_t>(z) * source_stride;
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, mip.size, tiled_size, info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail = tail;
|
||||
info.tail_x = tail ? mip.tail_x : 0;
|
||||
info.tail_y = tail ? mip.tail_y : 0;
|
||||
info.tiled_width = mip.padded_width;
|
||||
info.tiled_height = mip.padded_height;
|
||||
infos.push_back(info);
|
||||
tile_info.tiled_offset =
|
||||
mip.offset + static_cast<uint64_t>(slice_index) * source_stride;
|
||||
tile_info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
}
|
||||
if (!FitsBufferRange(tile_info.linear_offset, tile_info.linear_size, UINT64_MAX) ||
|
||||
!FitsBufferRange(tile_info.tiled_offset, tile_info.tiled_size, tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
const auto row_length =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto image_height = region.bufferImageHeight != 0 ? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
tile_info.width = std::max(
|
||||
(region.imageExtent.width + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.height =
|
||||
std::max((image_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
tile_info.pitch =
|
||||
std::max((row_length + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.tail = mip_level >= surface.first_tail_level;
|
||||
tile_info.tail_x = tile_info.tail ? mip.tail_x : 0;
|
||||
tile_info.tail_y = tile_info.tail ? mip.tail_y : 0;
|
||||
tile_info.tiled_width = mip.padded_width;
|
||||
tile_info.tiled_height = mip.padded_height;
|
||||
tile_infos.push_back(tile_info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
|
||||
if (infos.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_infos = std::move(infos);
|
||||
out_tile_infos = std::move(tile_infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
@@ -48,13 +46,13 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
|
||||
Prospero::ChannelOrder order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner);
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& infos);
|
||||
std::vector<GpuTileInfo>& out_tile_infos);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
+109
-132
@@ -28,7 +28,8 @@ namespace Libs::LibKernel::FileSystem {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
constexpr int DESCRIPTOR_MIN = 3;
|
||||
constexpr int DESCRIPTOR_MIN = 3;
|
||||
constexpr uint64_t DIR_BLOCK_SIZE = 512;
|
||||
|
||||
enum class SpecialFile {
|
||||
None,
|
||||
@@ -68,7 +69,7 @@ struct File {
|
||||
std::atomic_bool sync_writes;
|
||||
SpecialFile special;
|
||||
Common::Mutex mutex;
|
||||
std::vector<Common::File::DirEntry> dents;
|
||||
std::vector<uint8_t> dirents;
|
||||
uint64_t dents_offset;
|
||||
};
|
||||
|
||||
@@ -119,6 +120,51 @@ static uint64_t AlignUp(uint64_t value, uint64_t alignment) {
|
||||
return (alignment != 0 ? (value + alignment - 1) & ~(alignment - 1) : value);
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> PackDirents(const std::vector<Common::File::DirEntry>& entries) {
|
||||
std::vector<uint8_t> dirents;
|
||||
uint64_t offset = 0;
|
||||
uint64_t last_reclen_offset = 0;
|
||||
for (const auto& entry: entries) {
|
||||
const auto& name = entry.name;
|
||||
EXIT_NOT_IMPLEMENTED(name.size() > 255);
|
||||
const auto reclen = AlignUp(8 + name.size() + 1, 4);
|
||||
if (offset + reclen > dirents.size()) {
|
||||
if (!dirents.empty()) {
|
||||
auto* last_reclen =
|
||||
reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen += static_cast<uint16_t>(dirents.size() - offset);
|
||||
}
|
||||
offset = dirents.size();
|
||||
dirents.resize(offset + DIR_BLOCK_SIZE);
|
||||
}
|
||||
*reinterpret_cast<uint32_t*>(dirents.data() + offset) =
|
||||
Common::hash(name.data(), static_cast<uint32_t>(name.size()));
|
||||
*reinterpret_cast<uint16_t*>(dirents.data() + offset + 4) = static_cast<uint16_t>(reclen);
|
||||
dirents[offset + 6] = entry.is_file ? 8 : 4;
|
||||
dirents[offset + 7] = static_cast<uint8_t>(name.size());
|
||||
memcpy(dirents.data() + offset + 8, name.c_str(), name.size() + 1);
|
||||
last_reclen_offset = offset + 4;
|
||||
offset += reclen;
|
||||
}
|
||||
if (!dirents.empty()) {
|
||||
auto* last_reclen = reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen += static_cast<uint16_t>(dirents.size() - offset);
|
||||
}
|
||||
return dirents;
|
||||
}
|
||||
|
||||
static uint64_t ReadDirectory(File* file, void* buf, uint64_t count) {
|
||||
const auto remaining =
|
||||
file->dents_offset < file->dirents.size() ? file->dirents.size() - file->dents_offset : 0;
|
||||
const auto bytes = std::min(count, remaining);
|
||||
if (bytes != 0) {
|
||||
Memory::InvalidateMemory(reinterpret_cast<uint64_t>(buf), bytes);
|
||||
memcpy(buf, file->dirents.data() + file->dents_offset, bytes);
|
||||
file->dents_offset += bytes;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int PosixToKernel(int posix_errno) {
|
||||
return KERNEL_ERROR_UNKNOWN + posix_errno;
|
||||
}
|
||||
@@ -433,15 +479,16 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
|
||||
EXIT_NOT_IMPLEMENTED(!directory && rw_mode != Common::File::Mode::Read);
|
||||
EXIT_NOT_IMPLEMENTED(!directory && (trunc || creat));
|
||||
|
||||
file->dents = Common::File::GetDirEntries(file->real_name);
|
||||
const auto entries = Common::File::GetDirEntries(file->real_name);
|
||||
file->dirents = PackDirents(entries);
|
||||
file->dents_offset = 0;
|
||||
file->directory = true;
|
||||
|
||||
LOGF_COLOR(Log::Color::Green, "\tOpen dir: %s, entries = %" PRIu32 ", [ok]\n",
|
||||
Common::PathToString(file->real_name).c_str(),
|
||||
static_cast<uint32_t>(file->dents.size()));
|
||||
static_cast<uint32_t>(entries.size()));
|
||||
|
||||
for (const auto& f: file->dents) {
|
||||
for (const auto& f: entries) {
|
||||
LOGF("\t\t%s %s\n", f.is_file ? "[file]" : "[dir ]", f.name.c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -538,11 +585,18 @@ int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
if (nbytes > INT_MAX) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (file->directory) {
|
||||
Common::LockGuard lock(file->mutex);
|
||||
const auto bytes = ReadDirectory(file, buf, nbytes);
|
||||
LOGF("\tRead %" PRIu64 " directory bytes from: %s\n", bytes,
|
||||
Common::PathToString(file->real_name).c_str());
|
||||
return static_cast<int64_t>(bytes);
|
||||
}
|
||||
|
||||
if (file->special == SpecialFile::Random) {
|
||||
FillRandomBuffer(buf, nbytes);
|
||||
@@ -761,7 +815,7 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (d < DESCRIPTOR_MIN) {
|
||||
return KERNEL_ERROR_EPERM;
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
@@ -770,8 +824,6 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
if (file->special != SpecialFile::None) {
|
||||
@@ -780,38 +832,47 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
|
||||
Common::LockGuard lock(file->mutex);
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
|
||||
if (whence == 1) {
|
||||
offset = static_cast<int64_t>(file->f.Tell()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
if (whence == 2) {
|
||||
offset = static_cast<int64_t>(file->f.Size()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(whence != 0);
|
||||
|
||||
if (offset < 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
file->f.Seek(offset);
|
||||
auto pos = static_cast<int64_t>(file->f.Tell());
|
||||
|
||||
EXIT_IF(pos != offset);
|
||||
|
||||
if (is_invalid) {
|
||||
if (!file->directory && file->f.IsInvalid()) {
|
||||
LOGF("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
LOGF("\tLseek (pos = %" PRId64 ") to: %s\n", offset,
|
||||
uint64_t base = 0;
|
||||
switch (whence) {
|
||||
case 0: break;
|
||||
case 1: base = file->directory ? file->dents_offset : file->f.Tell(); break;
|
||||
case 2: base = file->directory ? file->dirents.size() : file->f.Size(); break;
|
||||
default: return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
uint64_t position = 0;
|
||||
if (offset >= 0) {
|
||||
if (base > static_cast<uint64_t>(INT64_MAX - offset)) {
|
||||
return KERNEL_ERROR_EOVERFLOW;
|
||||
}
|
||||
position = base + static_cast<uint64_t>(offset);
|
||||
} else {
|
||||
const auto distance = uint64_t {0} - static_cast<uint64_t>(offset);
|
||||
if (base < distance) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
position = base - distance;
|
||||
if (position > INT64_MAX) {
|
||||
return KERNEL_ERROR_EOVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->directory) {
|
||||
file->dents_offset = position;
|
||||
} else {
|
||||
file->f.Seek(position);
|
||||
EXIT_IF(file->f.Tell() != position);
|
||||
}
|
||||
|
||||
LOGF("\tLseek (pos = %" PRIu64 ") to: %s\n", position,
|
||||
Common::PathToString(file->real_name).c_str());
|
||||
|
||||
return pos;
|
||||
return static_cast<int64_t>(position);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
|
||||
@@ -842,9 +903,10 @@ int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
|
||||
auto wt = at;
|
||||
|
||||
if (is_dir) {
|
||||
stat.st_size = 0;
|
||||
stat.st_size =
|
||||
static_cast<int64_t>(PackDirents(Common::File::GetDirEntries(real_file_name)).size());
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = 0;
|
||||
stat.st_blocks = stat.st_size / 512;
|
||||
} else {
|
||||
stat.st_size = static_cast<int64_t>(Common::File::Size(real_file_name));
|
||||
stat.st_blksize = 512;
|
||||
@@ -920,9 +982,9 @@ int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb) {
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = (stat.st_size + 511) / 512;
|
||||
} else {
|
||||
stat.st_size = 0;
|
||||
stat.st_size = static_cast<int64_t>(file->dirents.size());
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = 0;
|
||||
stat.st_blocks = stat.st_size / 512;
|
||||
}
|
||||
|
||||
SecToTimespec(&stat.st_atim, at.ToUnix());
|
||||
@@ -1057,14 +1119,15 @@ int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* ba
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
constexpr uint64_t DIR_BLOCK_SIZE = 512;
|
||||
constexpr uint64_t DIRENT_META_SIZE = 8;
|
||||
|
||||
if (!file->directory || nbytes < static_cast<int>(DIR_BLOCK_SIZE)) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
Common::LockGuard lock(file->mutex);
|
||||
if (file->dents_offset > file->dirents.size() || file->dents_offset % DIR_BLOCK_SIZE != 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
LOGF("\t dir = %s\n"
|
||||
"\t nbytes = %d\n"
|
||||
@@ -1074,94 +1137,8 @@ int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* ba
|
||||
if (basep != nullptr) {
|
||||
*basep = static_cast<int64_t>(file->dents_offset);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> dirents;
|
||||
dirents.reserve(
|
||||
std::max<size_t>(static_cast<size_t>(DIR_BLOCK_SIZE), file->dents.size() * 64u));
|
||||
|
||||
uint64_t next_ceiling = 0;
|
||||
uint64_t dirent_offset = 0;
|
||||
int64_t last_reclen_offset = -1;
|
||||
|
||||
for (const auto& entry: file->dents) {
|
||||
const auto& str = entry.name;
|
||||
auto str_size = str.size();
|
||||
EXIT_NOT_IMPLEMENTED(str_size > 255);
|
||||
|
||||
uint64_t reclen = AlignUp(DIRENT_META_SIZE + str_size + 1, 4);
|
||||
if (dirent_offset + reclen > next_ceiling) {
|
||||
if (last_reclen_offset >= 0) {
|
||||
auto* last_reclen =
|
||||
reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen = static_cast<uint16_t>(*last_reclen + (next_ceiling - dirent_offset));
|
||||
}
|
||||
|
||||
dirent_offset = next_ceiling;
|
||||
next_ceiling += DIR_BLOCK_SIZE;
|
||||
dirents.resize(next_ceiling);
|
||||
std::fill(dirents.begin() + static_cast<std::ptrdiff_t>(dirent_offset), dirents.end(),
|
||||
0);
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t*>(dirents.data() + dirent_offset + 0) =
|
||||
Common::hash(str.data(), static_cast<uint32_t>(str.size()));
|
||||
*reinterpret_cast<uint16_t*>(dirents.data() + dirent_offset + 4) =
|
||||
static_cast<uint16_t>(reclen);
|
||||
*reinterpret_cast<uint8_t*>(dirents.data() + dirent_offset + 6) = (entry.is_file ? 8 : 4);
|
||||
*reinterpret_cast<uint8_t*>(dirents.data() + dirent_offset + 7) =
|
||||
static_cast<uint8_t>(str_size);
|
||||
memcpy(dirents.data() + dirent_offset + 8, str.data(), str_size);
|
||||
dirents[dirent_offset + 8 + str_size] = '\0';
|
||||
|
||||
last_reclen_offset = static_cast<int64_t>(dirent_offset + 4);
|
||||
dirent_offset += reclen;
|
||||
|
||||
LOGF("\t name = %s\n", str.data());
|
||||
}
|
||||
|
||||
if (last_reclen_offset >= 0) {
|
||||
auto* last_reclen = reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen = static_cast<uint16_t>(*last_reclen + (next_ceiling - dirent_offset));
|
||||
}
|
||||
|
||||
const uint64_t directory_size = next_ceiling;
|
||||
if (file->dents_offset >= directory_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t bytes_written = 0;
|
||||
uint64_t working_offset = file->dents_offset;
|
||||
uint64_t dirent_buffer_offset = 0;
|
||||
uint64_t aligned_count = AlignDown(static_cast<uint64_t>(nbytes), DIR_BLOCK_SIZE);
|
||||
|
||||
while (dirent_buffer_offset < dirents.size()) {
|
||||
const auto* normal_dirent = dirents.data() + dirent_buffer_offset;
|
||||
uint16_t reclen = *reinterpret_cast<const uint16_t*>(normal_dirent + 4);
|
||||
uint8_t namlen = *(normal_dirent + 7);
|
||||
|
||||
if (namlen == 0 || reclen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (working_offset >= reclen) {
|
||||
dirent_buffer_offset += reclen;
|
||||
working_offset -= reclen;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes_written + reclen > aligned_count) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto bytes_to_copy = reclen - working_offset;
|
||||
memcpy(buf + bytes_written, normal_dirent + working_offset, bytes_to_copy);
|
||||
bytes_written += bytes_to_copy;
|
||||
dirent_buffer_offset += reclen;
|
||||
working_offset = 0;
|
||||
}
|
||||
|
||||
file->dents_offset += bytes_written;
|
||||
return static_cast<int64_t>(bytes_written);
|
||||
return static_cast<int>(
|
||||
ReadDirectory(file, buf, AlignDown(static_cast<uint64_t>(nbytes), DIR_BLOCK_SIZE)));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetdents(int fd, char* buf, int nbytes) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "SDL.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
#include "kernel/fileSystem.h"
|
||||
#include "libs/errno.h"
|
||||
#include "libs/network.h"
|
||||
@@ -15,8 +17,11 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::LibKernelApr {
|
||||
void InitLibKernel_1_Apr(Loader::SymbolDatabase *symbols);
|
||||
@@ -123,6 +128,109 @@ void CheckMountRoot(const std::filesystem::path &root) {
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckDirectoryStream(const std::filesystem::path &root) {
|
||||
const auto directory = root / "directory-stream";
|
||||
Check(std::filesystem::create_directory(directory),
|
||||
"create seek fixture directory");
|
||||
for (int i = 0; i < 48; ++i) {
|
||||
Common::File fixture;
|
||||
Check(fixture.Create(directory / ("directory-entry-" + std::to_string(i))),
|
||||
"create enough entries to cross directory blocks");
|
||||
fixture.Close();
|
||||
}
|
||||
FileSystem::Mount(directory, "/app0");
|
||||
const int fd = FileSystem::KernelOpen("/app0/", 0, 0);
|
||||
Check(fd >= 3, "open directory as read-only asset");
|
||||
const auto end = FileSystem::KernelLseek(fd, 0, 2);
|
||||
Check(end > 512 && end % 512 == 0,
|
||||
"directory SEEK_END uses padded stream size");
|
||||
FileSystem::FileStat stat{};
|
||||
Check(FileSystem::KernelFstat(fd, &stat) == OK && stat.st_size == end &&
|
||||
stat.st_blksize == 512,
|
||||
"directory stat agrees with seek and enumeration");
|
||||
Check(FileSystem::KernelStat("/app0/", &stat) == OK && stat.st_size == end &&
|
||||
stat.st_blocks == end / 512,
|
||||
"path and descriptor directory stat agree");
|
||||
Check(FileSystem::KernelLseek(fd, 0, 0) == 0,
|
||||
"rewind directory for asset read");
|
||||
std::vector<char> raw(static_cast<size_t>(end));
|
||||
Check(FileSystem::KernelRead(fd, raw.data(), raw.size()) == end &&
|
||||
FileSystem::KernelRead(fd, raw.data(), 1) == 0,
|
||||
"raw directory read reaches EOF");
|
||||
Check(FileSystem::KernelLseek(fd, -end, 1) == 0,
|
||||
"directory SEEK_CUR uses the position advanced by read");
|
||||
|
||||
std::array<char, 512> block{};
|
||||
int64_t base = -1;
|
||||
for (int64_t offset = 0; offset < end; offset += block.size()) {
|
||||
Check(FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == block.size() &&
|
||||
base == offset &&
|
||||
std::memcmp(block.data(), raw.data() + offset, block.size()) == 0,
|
||||
"directory enumeration shares raw bytes and reports each block "
|
||||
"position");
|
||||
for (size_t pos = 0; pos < block.size();) {
|
||||
Check(block.size() - pos >= 8, "directory record header fits");
|
||||
uint16_t length = 0;
|
||||
std::memcpy(&length, block.data() + pos + 4, sizeof(length));
|
||||
const auto name_length = static_cast<uint8_t>(block[pos + 7]);
|
||||
Check(length >= 9 + name_length && length <= block.size() - pos &&
|
||||
block[pos + 8 + name_length] == '\0',
|
||||
"directory entries remain complete within every block");
|
||||
pos += length;
|
||||
}
|
||||
}
|
||||
Check(FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == 0 &&
|
||||
base == end,
|
||||
"directory enumeration reports EOF position");
|
||||
Check(FileSystem::KernelLseek(fd, 512, 0) == 512 &&
|
||||
FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == block.size() &&
|
||||
base == 512 &&
|
||||
std::memcmp(block.data(), raw.data() + 512, block.size()) == 0,
|
||||
"restore and reread a directory enumeration position");
|
||||
|
||||
const auto position = FileSystem::KernelLseek(fd, 0, 1);
|
||||
Check(
|
||||
FileSystem::KernelLseek(fd, 0, 9) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, -1, 0) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, std::numeric_limits<int64_t>::min(), 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, std::numeric_limits<int64_t>::max(), 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EOVERFLOW &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == position,
|
||||
"invalid and overflowing directory seeks preserve the position");
|
||||
Check(FileSystem::KernelLseek(fd, -19, 2) == end - 19 &&
|
||||
FileSystem::KernelRead(fd, block.data(), block.size()) == 19 &&
|
||||
std::memcmp(block.data(), raw.data() + end - 19, 19) == 0,
|
||||
"raw directory reads support byte positions and stop at EOF");
|
||||
Check(FileSystem::KernelLseek(fd, 1, 0) == 1 &&
|
||||
FileSystem::KernelGetdents(fd, block.data(), block.size()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == 1,
|
||||
"directory enumeration rejects an incomplete record position");
|
||||
Check(FileSystem::KernelLseek(fd, 512, 2) == end + 512 &&
|
||||
FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == end + 512,
|
||||
"directory enumeration rejects a position beyond EOF");
|
||||
Check(FileSystem::KernelRead(
|
||||
fd, block.data(),
|
||||
static_cast<size_t>(std::numeric_limits<int>::max()) + 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == end + 512,
|
||||
"oversized read fails without changing the directory position");
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close directory stream");
|
||||
Check(FileSystem::KernelLseek(fd, 0, 0) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EBADF,
|
||||
"seek rejects a closed descriptor");
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckAprPaths(const std::filesystem::path &root) {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibKernelApr::InitLibKernel_1_Apr(&symbols);
|
||||
@@ -267,15 +375,26 @@ int main() {
|
||||
Config::Load(options);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
|
||||
Check(SDL_InitSubSystem(SDL_INIT_VIDEO) == 0, "initialize Vulkan test video");
|
||||
auto graphics = std::make_unique<Libs::Graphics::WindowContext>();
|
||||
graphics->graphic_ctx.screen_width = 64;
|
||||
graphics->graphic_ctx.screen_height = 64;
|
||||
graphics->window = SDL_CreateWindow("KernelFileSystemTests", 0, 0, 64, 64,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN);
|
||||
Check(graphics->window != nullptr, "create hidden Vulkan test window");
|
||||
graphics->CreateVulkan();
|
||||
|
||||
TempDirectory temporary;
|
||||
FileSystem::Initialize();
|
||||
CheckMountRoot(temporary.Path());
|
||||
CheckDirectoryStream(temporary.Path());
|
||||
CheckAprPaths(temporary.Path());
|
||||
FileSystem::Mount(temporary.Path(), "/savedata0");
|
||||
CheckSaveRename(temporary.Path(), "first-save");
|
||||
CheckSaveRename(temporary.Path(), "replacement-save");
|
||||
FileSystem::Shutdown();
|
||||
CheckSocketWakeup();
|
||||
graphics.reset();
|
||||
subsystems.Destroy();
|
||||
|
||||
std::printf("KernelFileSystemTests: all cases passed\n");
|
||||
|
||||
Reference in New Issue
Block a user