feat(agc): implement guest texture resource decoding

This commit is contained in:
boykopovar 2026-09-23 08:00:22 +03:00
parent f50946adf0
commit 8e05ec2fa3
2 changed files with 137 additions and 6 deletions

View File

@ -13,7 +13,7 @@ Audio output and video output initialization pass.
Reaches the `main` function with render loop (deep call chain, ~64KB stack frame function) - init and shader loading from disk fully complete.
A standalone [SPIR-V triangle test](core/libs/prx/libSceAgcDriver/tests/Visual.cpp) passes GPU readback checks and presents a frame through the [libSceAgcDriver](core/libs/prx/libSceAgcDriver). The first application draw passes driver state validation and reaches shader recompilation.
Application shader execution remains unverified without completed [recompiler](core/shader/recompiler/Recompiler.cpp).
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces SPIR-V. The first application draw currently stops in [libSceAgcDriver](core/libs/prx/libSceAgcDriver), where support for shaders using textures is not implemented.
Now: `libSceAgcDriver: TextureDetiler not implemented`.

View File

@ -1,18 +1,149 @@
#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Context.hpp"
#include <stdexcept>
#include <string>
namespace AgcDriver::Graphics {
namespace {
void requireValidDstSel(std::uint32_t value) {
Require(value == 0 || value == 1 || (value >= 4 && value <= 7), "guest texture descriptor has an invalid destination channel selector");
}
TextureTileMode resolveTileMode(std::uint32_t raw) {
switch (raw) {
case 0x00: return TextureTileMode::kLinear;
case 0x01: return TextureTileMode::kStandard256B;
case 0x05: return TextureTileMode::kStandard4KB;
case 0x09: return TextureTileMode::kStandard64KB;
default: throw std::runtime_error("AGC graphics: guest texture descriptor uses an unsupported tile mode " + std::to_string(raw));
}
}
TextureDimension resolveDimension(std::uint32_t raw) {
switch (raw) {
case 8: return TextureDimension::k1D;
case 9: return TextureDimension::k2D;
case 11: return TextureDimension::kCube;
case 13: return TextureDimension::k2DArray;
default: throw std::runtime_error("AGC graphics: guest texture descriptor uses an unsupported image type " + std::to_string(raw));
}
}
}
GuestTextureResource DecodeTextureResource(std::span<const std::uint32_t> words) {
static_cast<void>(words);
throw std::runtime_error(std::string(__func__) + " not implemented");
Require(words.size() == 8, "guest texture descriptor must contain 8 dwords");
const auto base40 = (static_cast<std::uint64_t>(words[0]) | (static_cast<std::uint64_t>(words[1]) << 32u)) & 0xffffffffffull;
const auto baseAddress = base40 << 8u;
Require(baseAddress != 0, "guest texture descriptor has a null base address");
const auto minLod = (words[1] >> 8u) & 0xfffu;
const auto format = (words[1] >> 20u) & 0x1ffu;
const auto width = (((words[1] >> 30u) & 0x3u) | (((words[2] >> 0u) & 0xfffu) << 2u)) + 1u;
const auto height = ((words[2] >> 14u) & 0x3fffu) + 1u;
const auto dstSelX = (words[3] >> 0u) & 0x7u;
const auto dstSelY = (words[3] >> 3u) & 0x7u;
const auto dstSelZ = (words[3] >> 6u) & 0x7u;
const auto dstSelW = (words[3] >> 9u) & 0x7u;
const auto baseLevel = (words[3] >> 12u) & 0xfu;
const auto lastLevel = (words[3] >> 16u) & 0xfu;
const auto tileModeRaw = (words[3] >> 20u) & 0x1fu;
const auto bcSwizzle = (words[3] >> 25u) & 0x7u;
const auto typeRaw = (words[3] >> 28u) & 0xfu;
const auto depth = (words[4] >> 0u) & 0x1fffu;
const auto baseArray = (words[4] >> 16u) & 0x1fffu;
const auto arrayPitch = (words[5] >> 0u) & 0xfu;
const auto maxMip = (words[5] >> 4u) & 0xfu;
const auto minLodWarn = (words[5] >> 8u) & 0xfffu;
const auto perfMod = (words[5] >> 20u) & 0x7u;
const auto cornerSample = ((words[5] >> 23u) & 0x1u) != 0;
const auto mipStatsCntEn = ((words[5] >> 25u) & 0x1u) != 0;
const auto prtDefColor = ((words[5] >> 26u) & 0x1u) != 0;
const auto mipStatsCntId = words[6] & 0xffu;
const auto msaaDepth = ((words[6] >> 10u) & 0x1u) != 0;
const auto maxUncompBlkSize = (words[6] >> 15u) & 0x3u;
const auto maxCompBlkSize = (words[6] >> 17u) & 0x3u;
const auto metaPipeAligned = ((words[6] >> 19u) & 0x1u) != 0;
const auto writeCompress = ((words[6] >> 20u) & 0x1u) != 0;
const auto metaCompress = ((words[6] >> 21u) & 0x1u) != 0;
const auto dccAlphaPos = ((words[6] >> 22u) & 0x1u) != 0;
const auto dccColorTransf = ((words[6] >> 23u) & 0x1u) != 0;
const auto metaAddr = ((static_cast<std::uint64_t>(words[6]) >> 24u) & 0xffu) | (static_cast<std::uint64_t>(words[7]) << 8u);
requireValidDstSel(dstSelX);
requireValidDstSel(dstSelY);
requireValidDstSel(dstSelZ);
requireValidDstSel(dstSelW);
Require(minLod == 0, "guest texture descriptor uses a nonzero minimum LOD clamp which is not implemented");
Require(minLodWarn == 0, "guest texture descriptor uses a minimum LOD warning threshold which is not implemented");
Require(perfMod == 0, "guest texture descriptor uses a performance modulation value which is not implemented");
Require(mipStatsCntId == 0 && !mipStatsCntEn, "guest texture descriptor uses mip statistics counters which are not implemented");
Require(!cornerSample, "guest texture descriptor uses corner sampling which is not implemented");
Require(!prtDefColor, "guest texture descriptor uses a partially resident default color which is not implemented");
Require(arrayPitch == 0, "guest texture descriptor uses a nonzero array pitch which is not implemented");
Require(!msaaDepth, "guest texture descriptor uses MSAA which is not implemented");
Require(maxUncompBlkSize == 0 && maxCompBlkSize == 0, "guest texture descriptor uses DCC block size overrides which are not implemented");
Require(!metaPipeAligned && !writeCompress && !metaCompress && !dccAlphaPos && !dccColorTransf && metaAddr == 0, "guest texture descriptor uses metadata compression which is not implemented");
Require(bcSwizzle == 0, "guest texture descriptor uses a BC swizzle which is not implemented");
Require(baseLevel <= lastLevel, "guest texture descriptor has a base mip level past its last mip level");
Require(lastLevel == maxMip, "guest texture descriptor must expose every mip level down to the last one");
const auto tileMode = resolveTileMode(tileModeRaw);
const auto dimension = resolveDimension(typeRaw);
switch (dimension) {
case TextureDimension::k1D:
Require(height == 1 && depth == 0 && baseArray == 0, "guest 1D texture descriptor has a nonzero height, depth or base array");
break;
case TextureDimension::k2D:
Require(depth == 0 && baseArray == 0, "guest 2D texture descriptor has a nonzero depth or base array");
break;
case TextureDimension::k2DArray:
Require(baseArray <= depth, "guest 2D array texture descriptor has a base array past its last array slice");
break;
case TextureDimension::kCube:
Require(width == height, "guest cube texture descriptor is not square");
Require(baseArray <= depth, "guest cube texture descriptor has a base array past its last array slice");
Require((depth - baseArray + 1u) % 6u == 0, "guest cube texture descriptor does not contain a multiple of 6 array slices");
break;
}
GuestTextureResource result{};
result.baseAddress = baseAddress;
result.width = width;
result.height = height;
result.depthOrLastArray = depth;
result.baseArray = baseArray;
result.mipCount = maxMip + 1u;
result.baseLevel = baseLevel;
result.tileMode = tileMode;
result.dimension = dimension;
result.format = format;
result.dstSelX = static_cast<std::uint8_t>(dstSelX);
result.dstSelY = static_cast<std::uint8_t>(dstSelY);
result.dstSelZ = static_cast<std::uint8_t>(dstSelZ);
result.dstSelW = static_cast<std::uint8_t>(dstSelW);
return result;
}
bool MatchesGuestDimension(ShaderRecompiler::DescriptorImageShape shape, TextureDimension dimension) {
static_cast<void>(shape);
static_cast<void>(dimension);
throw std::runtime_error(std::string(__func__) + " not implemented");
switch (shape) {
case ShaderRecompiler::DescriptorImageShape::Image1D: return dimension == TextureDimension::k1D;
case ShaderRecompiler::DescriptorImageShape::Image2D: return dimension == TextureDimension::k2D;
case ShaderRecompiler::DescriptorImageShape::Image2DArray: return dimension == TextureDimension::k2DArray;
case ShaderRecompiler::DescriptorImageShape::ImageCube: return dimension == TextureDimension::kCube;
case ShaderRecompiler::DescriptorImageShape::Image3D: return false;
}
throw std::runtime_error("AGC graphics: MatchesGuestDimension encountered an unknown descriptor image shape");
}
}