Merge pull request #4318 from Amoenus/codex/fix-color-target-d64-x
feat(agc): support 64 KiB display XOR color targets
This commit is contained in:
commit
6f438a1c2e
|
|
@ -1213,6 +1213,12 @@ if(BUILD_TESTING)
|
|||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
set_tests_properties(agc_driver_provoking_vertex PROPERTIES TIMEOUT 30)
|
||||
add_test(NAME agc_driver_uint8x4_color_target_no_resident COMMAND agc_driver_uint8x4_color_target_tests)
|
||||
set_tests_properties(agc_driver_uint8x4_color_target_no_resident PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_RESIDENT_TARGETS=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
|
||||
if(WIN32)
|
||||
set_tests_properties(agc_driver_uint8x4_color_target_no_resident PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
set_tests_properties(agc_driver_flat_store PROPERTIES RUN_SERIAL TRUE)
|
||||
set_target_properties(agc_driver_flat_apertures_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
|
||||
foreach(flatAperturesPart RANGE 13)
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ enum class ColorTileMode : std::uint32_t {
|
|||
Standard64KB = 9,
|
||||
S4KBX = 0x15,
|
||||
D4KBX = 0x16,
|
||||
D64KBX = 0x1a,
|
||||
RenderTarget = 0x1b
|
||||
};
|
||||
|
||||
constexpr bool ColorTileModeIsXor(ColorTileMode mode) {
|
||||
return mode == ColorTileMode::S4KBX || mode == ColorTileMode::D4KBX || mode == ColorTileMode::RenderTarget;
|
||||
return mode == ColorTileMode::S4KBX || mode == ColorTileMode::D4KBX || mode == ColorTileMode::D64KBX || mode == ColorTileMode::RenderTarget;
|
||||
}
|
||||
|
||||
constexpr std::uint32_t ColorTileModeBlockBytes(ColorTileMode mode) {
|
||||
|
|
@ -30,7 +31,7 @@ constexpr std::uint32_t ColorTileModeBlockBytes(ColorTileMode mode) {
|
|||
}
|
||||
|
||||
constexpr TextureTileMode ColorTextureTileMode(ColorTileMode mode) {
|
||||
return mode == ColorTileMode::Linear ? TextureTileMode::kLinear : mode == ColorTileMode::Standard4KB ? TextureTileMode::kStandard4KB : mode == ColorTileMode::Standard64KB ? TextureTileMode::kStandard64KB : mode == ColorTileMode::S4KBX ? TextureTileMode::kS4KBX : mode == ColorTileMode::D4KBX ? TextureTileMode::kD4KBX : TextureTileMode::kR64KBX;
|
||||
return mode == ColorTileMode::Linear ? TextureTileMode::kLinear : mode == ColorTileMode::Standard4KB ? TextureTileMode::kStandard4KB : mode == ColorTileMode::Standard64KB ? TextureTileMode::kStandard64KB : mode == ColorTileMode::S4KBX ? TextureTileMode::kS4KBX : mode == ColorTileMode::D4KBX ? TextureTileMode::kD4KBX : mode == ColorTileMode::D64KBX ? TextureTileMode::kD64KBX : TextureTileMode::kR64KBX;
|
||||
}
|
||||
|
||||
ColorTileMode DecodeColorTileMode(std::uint32_t attrib3);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ ColorTileMode DecodeColorTileMode(std::uint32_t attrib3) {
|
|||
require((attrib3 & 0x80002000u) == 0 && ((resourceType == 0 && (attrib3 & 0x1fffu) == 0) || resourceType == 1 || resourceType == 2) && ((attrib3 >> 27u) & 7u) == 1, "AGC graphics: unsupported color depth, dimension, resource level or metadata mode");
|
||||
const auto mode = (attrib3 >> 14u) & 0x1fu;
|
||||
const auto fmaskMode = (attrib3 >> 19u) & 0x1fu;
|
||||
require(mode == 0 || mode == 5 || mode == 9 || mode == 0x15 || mode == 0x16 || mode == 0x1b, "AGC graphics: unsupported color tile mode");
|
||||
require(mode == 0 || mode == 5 || mode == 9 || mode == 0x15 || mode == 0x16 || mode == 0x1a || mode == 0x1b, "AGC graphics: unsupported color tile mode");
|
||||
require(fmaskMode == 0 || fmaskMode == 0x18 || fmaskMode == (mode & ~3u), "AGC graphics: unsupported color FMASK swizzle mode");
|
||||
require(resourceType != 0 || mode == 0 || mode == 0x1b, "AGC graphics: 1D color targets with a standard swizzle mode are invalid");
|
||||
return static_cast<ColorTileMode>(mode);
|
||||
|
|
@ -74,9 +74,9 @@ struct SwizzleTables {
|
|||
};
|
||||
|
||||
const SwizzleTables& equationTables(ColorTileMode mode, std::uint32_t bytesPerElement, std::uint32_t blockWidth, std::uint32_t blockHeight) {
|
||||
static std::once_flag once[3][5];
|
||||
static SwizzleTables tables[3][5];
|
||||
const auto modeIndex = mode == ColorTileMode::S4KBX ? 0u : mode == ColorTileMode::D4KBX ? 1u : 2u;
|
||||
static std::once_flag once[4][5];
|
||||
static SwizzleTables tables[4][5];
|
||||
const auto modeIndex = mode == ColorTileMode::S4KBX ? 0u : mode == ColorTileMode::D4KBX ? 1u : mode == ColorTileMode::D64KBX ? 2u : 3u;
|
||||
const auto index = static_cast<std::size_t>(std::countr_zero(bytesPerElement));
|
||||
std::call_once(once[modeIndex][index], [&] {
|
||||
const auto* equation = FindTextureSwizzleEquation(static_cast<std::uint32_t>(mode), bytesPerElement);
|
||||
|
|
@ -118,6 +118,7 @@ ColorTargetLayout::ColorTargetLayout(std::uint32_t width, std::uint32_t height,
|
|||
require(width != 0 && height != 0 && width <= 16384 && height <= 16384, "AGC graphics: invalid color surface extent");
|
||||
require(std::has_single_bit(bytesPerElement) && bytesPerElement <= 16u, "AGC graphics: unsupported color element size");
|
||||
require(pipeBankXor == 0 || (ColorTileModeIsXor(mode) && pipeBankXor < ColorTileModeBlockBytes(mode) && pipeBankXor % 256u == 0), "AGC graphics: a color pipe/bank XOR applies only to whole 256-byte units of XOR swizzle blocks");
|
||||
require(mode != ColorTileMode::D64KBX || (pipeBankXor & ~0xcf00u) == 0, "AGC graphics: unsupported display color pipe/bank XOR column bits");
|
||||
std::uint32_t paddedHeight = height;
|
||||
switch (mode) {
|
||||
case ColorTileMode::Linear: {
|
||||
|
|
@ -127,9 +128,9 @@ ColorTargetLayout::ColorTargetLayout(std::uint32_t width, std::uint32_t height,
|
|||
}
|
||||
case ColorTileMode::S4KBX:
|
||||
case ColorTileMode::D4KBX:
|
||||
case ColorTileMode::D64KBX:
|
||||
case ColorTileMode::RenderTarget: {
|
||||
// SW_64KB_R_X: 64 KiB blocks of 2^(16 - log2(bpe)) elements, wider than tall for odd powers.
|
||||
const auto log2Elements = (mode == ColorTileMode::RenderTarget ? 16u : 12u) - static_cast<std::uint32_t>(std::countr_zero(bytesPerElement));
|
||||
const auto log2Elements = static_cast<std::uint32_t>(std::countr_zero(ColorTileModeBlockBytes(mode))) - static_cast<std::uint32_t>(std::countr_zero(bytesPerElement));
|
||||
blockWidth = 1u << ((log2Elements + 1u) / 2u);
|
||||
blockHeight = 1u << (log2Elements / 2u);
|
||||
pitch = (width + blockWidth - 1u) / blockWidth * blockWidth;
|
||||
|
|
|
|||
|
|
@ -1040,6 +1040,7 @@ ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot) {
|
|||
color.pipeBankXor = static_cast<std::uint32_t>(color.surfaceAddress & (ColorTileModeBlockBytes(color.tileMode) - 1u));
|
||||
color.surfaceAddress -= color.pipeBankXor;
|
||||
}
|
||||
Require(color.tileMode != ColorTileMode::D64KBX || (color.pipeBankXor & ~0xcf00u) == 0, "unsupported display color pipe/bank XOR column bits");
|
||||
color.arrayAddress = color.arrayLastSlice != 0 ? color.surfaceAddress : 0;
|
||||
if (slice != 0 && !volume) color.surfaceAddress += slice * ComputeSurfaceSize(ComputeElementMipLayout(ColorTextureTileMode(color.tileMode), color.elementBytes, color.surfaceExtent.width, color.surfaceExtent.height, color.mipCount), 1);
|
||||
color.address = color.surfaceAddress + mipOffset;
|
||||
|
|
|
|||
|
|
@ -207,4 +207,55 @@ void RunColorTargetLayoutTests() {
|
|||
Require(back == source, "4 KiB XOR color tiling round trip lost pixels");
|
||||
reject([mode] { static_cast<void>(ColorTargetLayout(40, 20, mode, 4, 0x1000)); });
|
||||
}
|
||||
|
||||
Require(DecodeColorTileMode(0x0dc68000u) == ColorTileMode::D64KBX, "the observed SW_64KB_D_X color descriptor was rejected");
|
||||
Require(ColorTextureTileMode(ColorTileMode::D64KBX) == TextureTileMode::kD64KBX, "a display XOR color target did not map to its texture mode");
|
||||
Require(ColorTileModeIsXor(ColorTileMode::D64KBX) && ColorTileModeBlockBytes(ColorTileMode::D64KBX) == 65536u, "a display XOR color target lost its block alignment or XOR flag");
|
||||
reject([] { DecodeColorTileMode(0x0cc68000u); });
|
||||
reject([] { DecodeColorTileMode((0x0dc68000u & ~(0x1fu << 19u)) | (1u << 19u)); });
|
||||
reject([] { ColorTargetLayout(64, 64, ColorTileMode::D64KBX, 4, 0x80); });
|
||||
reject([] { ColorTargetLayout(64, 64, ColorTileMode::D64KBX, 4, 0x10000); });
|
||||
for (const std::uint32_t columnBits : {0x1000u, 0x2000u, 0x3000u}) reject([columnBits] { ColorTargetLayout(64, 64, ColorTileMode::D64KBX, 4, columnBits); });
|
||||
const std::array<std::array<std::size_t, 2>, 5> sourceOffsets{{{27647u, 66048u}, {29694u, 212992u}, {62204u, 344064u}, {45304u, 540672u}, {110832u, 933888u}}};
|
||||
for (const std::uint32_t bpe : {1u, 2u, 4u, 8u, 16u}) {
|
||||
const ColorTargetLayout sourceLayout(257, 129, ColorTileMode::D64KBX, bpe, 0x4000);
|
||||
const auto& offsets = sourceOffsets[std::countr_zero(bpe)];
|
||||
for (std::uint32_t xorTag = 0; xorTag < 256u; ++xorTag) {
|
||||
if ((xorTag & ~0xcfu) != 0) continue;
|
||||
const ColorTargetLayout xorLayout(1, 1, ColorTileMode::D64KBX, bpe, xorTag << 8u);
|
||||
Require(xorLayout.Offset(0, 0) == (xorTag << 8u), "a display XOR color origin differs from its pipe/bank bits");
|
||||
}
|
||||
Require(sourceLayout.Offset(127, 63) == offsets[0] && sourceLayout.Offset(256, 128) == offsets[1], "display XOR color offsets differ from the AMD GFX10 non-RB+ source reference");
|
||||
const auto block = ThinBlockLayout(TextureTileMode::kD64KBX, bpe);
|
||||
const auto width = 2u * block[1] + 1u;
|
||||
const auto height = 2u * block[2] + 1u;
|
||||
for (const auto mode : {ColorTileMode::D64KBX, ColorTileMode::RenderTarget}) {
|
||||
const auto* equation = FindTextureSwizzleEquation(static_cast<std::uint32_t>(mode), bpe);
|
||||
Require(equation != nullptr, "a 64 KiB color swizzle equation is missing");
|
||||
const ColorTargetLayout layout(width, height, mode, bpe, 0x4600);
|
||||
Require(layout.Alignment() == block[0] && layout.BlocksPerRow() == 3u && layout.Bytes() == ComputeSurfaceSize(ComputeElementMipLayout(ColorTextureTileMode(mode), bpe, width, height, 1), 1), "a 64 KiB color layout differs from its texture geometry");
|
||||
for (const auto x : {0u, 1u, block[1] - 1u, block[1], block[1] + 1u, width - 1u}) {
|
||||
for (const auto y : {0u, 1u, block[2] - 1u, block[2], block[2] + 1u, height - 1u}) {
|
||||
std::size_t expected = 0;
|
||||
for (std::uint32_t bit = 0; bit < 16u; ++bit) expected |= static_cast<std::size_t>(std::popcount((x & equation->bits[bit] & 0xfffu) ^ ((y << 12u) & equation->bits[bit] & 0xfff000u)) & 1) << bit;
|
||||
const auto blockIndex = (y / block[2]) * 3u + x / block[1];
|
||||
Require(layout.Offset(x, y) == blockIndex * 65536u + (expected ^ 0x4600u), "a 64 KiB color offset differs from its mode's equation across block boundaries");
|
||||
}
|
||||
}
|
||||
std::vector<std::byte> source(layout.LinearBytes());
|
||||
for (std::size_t index = 0; index < source.size(); ++index) source[index] = static_cast<std::byte>((index * 31u + index / 251u) & 255u);
|
||||
std::vector<std::byte> tiled(layout.Bytes());
|
||||
std::vector<std::byte> restored(source.size());
|
||||
layout.Tile(source, tiled);
|
||||
layout.Detile(tiled, restored);
|
||||
Require(restored == source, "64 KiB display/render-target XOR color tiling lost pixels");
|
||||
}
|
||||
}
|
||||
std::fill(guest.begin(), guest.end(), std::byte{0x6b});
|
||||
target = {reinterpret_cast<std::uintptr_t>(guest.data()), {2, 2}, VK_FORMAT_R8G8B8A8_UNORM, guest.size(), 0xe4, ColorTileMode::D64KBX};
|
||||
target.pipeBankXor = 0x0200;
|
||||
WriteColorTarget(target, pixels);
|
||||
readback.fill(std::byte{0});
|
||||
ReadColorTarget(target, readback);
|
||||
Require(readback == pixels && guest[0] == std::byte{0x6b}, "display XOR guest color transfer lost pixels or wrote the untagged block origin");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -814,6 +814,30 @@ void ColorPipeBankXorTests() {
|
|||
const auto color = AgcDriver::Graphics::DecodeState(queue).color;
|
||||
Require(color.tileMode == AgcDriver::Graphics::ColorTileMode::D4KBX && color.address == block + 0x3000u && color.pipeBankXor == pipeBankXor, "a SW_4KB_D_X color base did not split into its 4 KiB block base and pipe/bank XOR " + std::to_string(pipeBankXor));
|
||||
}
|
||||
queue.context[0x3b8] = 0x0dc68000u;
|
||||
for (const std::uint32_t pipeBankXor : {0u, 0x4600u, 0xcf00u}) {
|
||||
const auto address = block + pipeBankXor;
|
||||
queue.context[0x318] = static_cast<std::uint32_t>(address >> 8u);
|
||||
queue.context[0x390] = static_cast<std::uint32_t>(address >> 40u);
|
||||
const auto color = AgcDriver::Graphics::DecodeState(queue).color;
|
||||
Require(color.tileMode == AgcDriver::Graphics::ColorTileMode::D64KBX && color.address == block && color.surfaceAddress == block && color.pipeBankXor == pipeBankXor, "a SW_64KB_D_X color base did not split its block address and XOR");
|
||||
}
|
||||
for (const std::uint32_t columnBits : {0x1000u, 0x2000u, 0x3000u}) {
|
||||
queue.context[0x318] = static_cast<std::uint32_t>((block + columnBits) >> 8u);
|
||||
queue.context[0x390] = static_cast<std::uint32_t>((block + columnBits) >> 40u);
|
||||
expectFailure([&queue] { AgcDriver::Graphics::DecodeColorBuffer(queue.context, 0); }, "unsupported display color pipe/bank XOR column bits");
|
||||
}
|
||||
static std::vector<std::byte> mipStorage(16u * blockBytes);
|
||||
const auto mipBase = (reinterpret_cast<std::uintptr_t>(mipStorage.data()) + blockBytes - 1u) / blockBytes * blockBytes;
|
||||
const auto mips = AgcDriver::Graphics::ComputeElementMipLayout(AgcDriver::Graphics::TextureTileMode::kD64KBX, 4, 256, 128, 5);
|
||||
const auto sliceBytes = AgcDriver::Graphics::ComputeSurfaceSize(mips, 1);
|
||||
queue.context[0x318] = static_cast<std::uint32_t>((mipBase + 0x0200u) >> 8u);
|
||||
queue.context[0x390] = static_cast<std::uint32_t>((mipBase + 0x0200u) >> 40u);
|
||||
queue.context[0x3b0] = (4u << 28u) | (255u << 14u) | 127u;
|
||||
queue.context[0x3b8] |= 1u;
|
||||
queue.context[0x31b] = (2u << 26u) | 1u | (1u << 13u);
|
||||
const auto mipColor = AgcDriver::Graphics::DecodeColorBuffer(queue.context, 0);
|
||||
Require(mipColor.mip == 2 && mipColor.mipCount == 5 && mipColor.extent.width == 64 && mipColor.extent.height == 32 && mipColor.surfaceAddress == mipBase + sliceBytes && mipColor.address == mipBase + sliceBytes + mips[2].tiledOffset && mipColor.mipTail == mips[2].tail && mipColor.pipeBankXor == 0x0200u, "a display XOR color view lost its array slice, mip placement or XOR");
|
||||
}
|
||||
|
||||
void HiddenUserWordVertexTablesTests() {
|
||||
|
|
|
|||
|
|
@ -113,12 +113,12 @@ private:
|
|||
std::uint8_t* block = nullptr;
|
||||
};
|
||||
|
||||
std::array<std::uint32_t, 8> ArrayDescriptor(std::uint64_t address) {
|
||||
std::array<std::uint32_t, 8> ArrayDescriptor(std::uint64_t address, std::uint32_t swizzleMode) {
|
||||
return {
|
||||
static_cast<std::uint32_t>(address >> 8u),
|
||||
static_cast<std::uint32_t>((address >> 40u) & 0xffu) | (Format32Float << 20u) | (((Side - 1u) & 3u) << 30u),
|
||||
((Side - 1u) >> 2u) | ((Side - 1u) << 14u),
|
||||
0xfacu | (TileR64KBX << 20u) | (Type2DArray << 28u),
|
||||
0xfacu | (swizzleMode << 20u) | (Type2DArray << 28u),
|
||||
Layers - 1u,
|
||||
0u,
|
||||
0u,
|
||||
|
|
@ -146,13 +146,13 @@ void Dispatch(AgcDriver::VulkanDevice& device, std::span<const std::uint32_t> co
|
|||
device.WaitIdle();
|
||||
}
|
||||
|
||||
void LoadLayer(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t layer) {
|
||||
void LoadLayer(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t layer, std::uint32_t swizzleMode) {
|
||||
Output.fill(0xdeadbeefu);
|
||||
auto& code = LoadCodes[layer];
|
||||
code = {0x7e020280, 0x7e040280u | layer, 0xf0001128, 0x00010300, 0x34080082, 0xbf8c3f70, 0xe0701000, 0x80000304, 0xbf810000};
|
||||
std::vector<std::uint32_t> userData(16, 0u);
|
||||
const auto buffer = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
|
||||
const auto texture = ArrayDescriptor(surface);
|
||||
const auto texture = ArrayDescriptor(surface, swizzleMode);
|
||||
std::copy(buffer.begin(), buffer.end(), userData.begin());
|
||||
std::copy(texture.begin(), texture.end(), userData.begin() + 4);
|
||||
Dispatch(device, code, userData);
|
||||
|
|
@ -165,14 +165,14 @@ void RequireOutput(std::uint32_t inside, std::uint32_t outside, const std::strin
|
|||
}
|
||||
}
|
||||
|
||||
AgcDriver::Graphics::ColorTarget DecodeTarget(std::uint64_t address, std::uint32_t slice) {
|
||||
AgcDriver::Graphics::ColorTarget DecodeTarget(std::uint64_t address, std::uint32_t slice, std::uint32_t swizzleMode) {
|
||||
AgcDriver::Registers cx;
|
||||
cx[0x318] = static_cast<std::uint32_t>(address >> 8u);
|
||||
cx[0x31b] = slice | (slice << 13u);
|
||||
cx[0x31c] = Color32FloatInfo;
|
||||
cx[0x31d] = 0;
|
||||
cx[0x3b0] = ((Side - 1u) << 14u) | (Side - 1u);
|
||||
cx[0x3b8] = TiledAttrib3 | (Layers - 1u);
|
||||
cx[0x3b8] = (TiledAttrib3 & ~(0x1fu << 14u)) | (swizzleMode << 14u) | (Layers - 1u);
|
||||
cx[0x390] = static_cast<std::uint32_t>(address >> 40u);
|
||||
const auto color = AgcDriver::Graphics::DecodeColorBuffer(cx, 0);
|
||||
Require(color.format == VK_FORMAT_R32_SFLOAT && color.bytes == LayerBytes && color.address == address + slice * LayerBytes, "the COLOR_32 float view did not decode as a 128x128 R32 slice of the array");
|
||||
|
|
@ -180,7 +180,7 @@ AgcDriver::Graphics::ColorTarget DecodeTarget(std::uint64_t address, std::uint32
|
|||
return color;
|
||||
}
|
||||
|
||||
void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t slice) {
|
||||
void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t slice, std::uint32_t swizzleMode) {
|
||||
const auto target = device.Target();
|
||||
constexpr std::uint32_t waveSize = 64;
|
||||
std::size_t at = 0;
|
||||
|
|
@ -207,7 +207,7 @@ void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint
|
|||
const auto vertexResult = ShaderRecompiler::Recompile(vertex);
|
||||
const auto vertexPush = static_cast<std::uint32_t>(vertexResult.pushConstants.size());
|
||||
|
||||
const auto color = DecodeTarget(surface, slice);
|
||||
const auto color = DecodeTarget(surface, slice, swizzleMode);
|
||||
VkPipelineColorBlendAttachmentState blend{};
|
||||
blend.colorWriteMask = 0xf;
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
@ -246,20 +246,20 @@ void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint
|
|||
device.WaitIdle();
|
||||
}
|
||||
|
||||
void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels) {
|
||||
void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) {
|
||||
const auto surface = AddressOf(texels);
|
||||
std::fill_n(reinterpret_cast<std::uint32_t*>(texels), SurfaceBytes / 4u, Initial);
|
||||
AgcDriver::GuestMemory::CollectWrites(surface, SurfaceBytes);
|
||||
AgcDriver::GuestMemory::BumpCollectEpoch();
|
||||
std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex());
|
||||
const auto drawn = std::bit_cast<std::uint32_t>(Drawn);
|
||||
DrawSlice(device, surface, 1);
|
||||
DrawSlice(device, surface, 1, swizzleMode);
|
||||
const auto pending = StorageTexture::FindPending(surface, SurfaceBytes);
|
||||
Require(pending != nullptr && pending->Descriptor().dimension == TextureDimension::k2DArray && pending->Descriptor().baseAddress == surface && pending->Descriptor().depthOrLastArray == Layers - 1u, "the draw into slice 1 did not render into the image of its array");
|
||||
LoadLayer(device, surface, 1);
|
||||
LoadLayer(device, surface, 1, swizzleMode);
|
||||
RequireOutput(drawn, Initial, "slice 1 read through the array after the draw");
|
||||
for (const std::uint32_t other : {0u, 2u, 3u}) {
|
||||
LoadLayer(device, surface, other);
|
||||
LoadLayer(device, surface, other, swizzleMode);
|
||||
RequireOutput(Initial, Initial, "slice " + std::to_string(other) + " after the draw into slice 1");
|
||||
}
|
||||
StorageTexture::FlushPending(surface, SurfaceBytes, nullptr, "test");
|
||||
|
|
@ -267,7 +267,7 @@ void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels) {
|
|||
AgcDriver::Graphics::ClearCachedTextures(device.Device());
|
||||
AgcDriver::GuestMemory::CollectWrites(surface, SurfaceBytes);
|
||||
AgcDriver::GuestMemory::BumpCollectEpoch();
|
||||
LoadLayer(device, surface, 1);
|
||||
LoadLayer(device, surface, 1, swizzleMode);
|
||||
RequireOutput(drawn, Initial, "slice 1 uploaded again from guest memory");
|
||||
AgcDriver::Graphics::ClearCachedTextures(device.Device());
|
||||
}
|
||||
|
|
@ -279,7 +279,8 @@ int main() {
|
|||
GuestBlock block;
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
Run(*device, block.Data());
|
||||
Run(*device, block.Data(), TileR64KBX);
|
||||
Run(*device, block.Data(), static_cast<std::uint32_t>(AgcDriver::Graphics::ColorTileMode::D64KBX));
|
||||
std::puts("array layer target tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
|
|
|
|||
|
|
@ -133,17 +133,18 @@ struct Block {
|
|||
std::uint8_t* data = nullptr;
|
||||
};
|
||||
|
||||
AgcDriver::Graphics::ColorTarget DecodeTarget(const Block& block, std::uint32_t info) {
|
||||
AgcDriver::Graphics::ColorTarget DecodeTarget(const Block& block, std::uint32_t info, AgcDriver::Graphics::ColorTileMode mode, std::uint32_t pipeBankXor) {
|
||||
AgcDriver::Registers cx;
|
||||
cx[0x318] = static_cast<std::uint32_t>(block.Address() >> 8u);
|
||||
const auto address = block.Address() + pipeBankXor;
|
||||
cx[0x318] = static_cast<std::uint32_t>(address >> 8u);
|
||||
cx[0x31b] = 0;
|
||||
cx[0x31c] = info;
|
||||
cx[0x31d] = 0;
|
||||
cx[0x323] = 0;
|
||||
cx[0x324] = 0;
|
||||
cx[0x3b0] = ((Width - 1u) << 14u) | (Height - 1u);
|
||||
cx[0x3b8] = LinearAttrib3;
|
||||
cx[0x390] = static_cast<std::uint32_t>(block.Address() >> 40u);
|
||||
cx[0x3b8] = LinearAttrib3 | (static_cast<std::uint32_t>(mode) << 14u);
|
||||
cx[0x390] = static_cast<std::uint32_t>(address >> 40u);
|
||||
auto color = AgcDriver::Graphics::DecodeColorBuffer(cx, 0);
|
||||
color.exportIndex = 0;
|
||||
color.uintExport = true;
|
||||
|
|
@ -218,15 +219,17 @@ std::vector<std::uint8_t> ReadBack(AgcDriver::VulkanDevice& device, std::uint64_
|
|||
return stored;
|
||||
}
|
||||
|
||||
void Expect(AgcDriver::VulkanDevice& device, std::uint32_t info, VkFormat format, std::uint32_t elementBytes, std::array<std::uint16_t, 4> exported, const std::string& what) {
|
||||
void Expect(AgcDriver::VulkanDevice& device, std::uint32_t info, VkFormat format, std::uint32_t elementBytes, std::array<std::uint16_t, 4> exported, const std::string& what, AgcDriver::Graphics::ColorTileMode mode = AgcDriver::Graphics::ColorTileMode::Linear, std::uint32_t pipeBankXor = 0) {
|
||||
const auto bytes = static_cast<std::size_t>(Width) * Height * elementBytes;
|
||||
Require(AgcDriver::Graphics::ColorTargetLayout(Width, Height, AgcDriver::Graphics::ColorTileMode::Linear, elementBytes).Bytes() == bytes, what + ": the linear target is padded");
|
||||
Block block(bytes);
|
||||
std::memset(block.data, Kept, bytes);
|
||||
const auto color = DecodeTarget(block, info);
|
||||
Require(color.format == format && color.elementBytes == elementBytes && color.bytes == bytes, what + ": the target decoded to the wrong format");
|
||||
const AgcDriver::Graphics::ColorTargetLayout layout(Width, Height, mode, elementBytes, pipeBankXor);
|
||||
Block block(layout.Bytes());
|
||||
std::memset(block.data, Kept, block.bytes);
|
||||
const auto color = DecodeTarget(block, info, mode, pipeBankXor);
|
||||
Require(color.format == format && color.elementBytes == elementBytes && color.bytes == layout.Bytes() && color.tileMode == mode && color.pipeBankXor == pipeBankXor, what + ": the target decoded to the wrong format, mode or XOR");
|
||||
Draw(device, color, Program(exported));
|
||||
const auto stored = ReadBack(device, block.Address(), bytes);
|
||||
const auto tiled = ReadBack(device, block.Address(), block.bytes);
|
||||
std::vector<std::uint8_t> stored(bytes);
|
||||
layout.Detile(std::as_bytes(std::span(tiled)), std::as_writable_bytes(std::span(stored)));
|
||||
for (std::size_t offset = 0; offset < bytes; ++offset) {
|
||||
const bool inside = (offset / elementBytes) % Width < ScissorWidth;
|
||||
const auto expected = inside ? static_cast<std::uint8_t>(exported[offset % elementBytes]) : Kept;
|
||||
|
|
@ -241,6 +244,8 @@ int main() {
|
|||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
Expect(*device, Rgba8UintInfo, VK_FORMAT_R8G8B8A8_UINT, 4, {0x12u, 0xbeu, 0x42u, 0x7fu}, "a UINT16_ABGR export into an 8_8_8_8_UINT target");
|
||||
Expect(*device, Rgba8UintInfo, VK_FORMAT_R8G8B8A8_UINT, 4, {0x23u, 0x9au, 0x65u, 0x81u}, "a UINT16_ABGR export into a SW_64KB_D_X target", AgcDriver::Graphics::ColorTileMode::D64KBX);
|
||||
Expect(*device, Rgba8UintInfo, VK_FORMAT_R8G8B8A8_UINT, 4, {0x31u, 0x7cu, 0x42u, 0x96u}, "a UINT16_ABGR export into a SW_64KB_D_X target with XOR", AgcDriver::Graphics::ColorTileMode::D64KBX, 0x4600);
|
||||
std::puts("8_8_8_8_UINT color target tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
### Functional
|
||||
|
||||
- [Reused depth storage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp): a storage view with another extent or a format that cannot hold the cached depth plane uses guest memory. Depth rendering is not written back to those guest bytes; this admission rule does not prove allocation reuse or preserve a depth writer through an incompatible view. Matching depth/stencil storage access still throws.
|
||||
- [SW_64KB_D_X color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ColorTargetLayout.cpp) (libSceAgcDriver) use the existing texture block/mip geometry and equations from [AMD addrlib GFX10 non-RB+](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp) for 16 pipes and 256-byte interleave. The address-reference tests use legal pipe/bank XOR bits for that configuration. Raw column bits outside the source configuration's pipe/bank mask 0xcf00 throw; their console meaning is unresolved. Console address equations and render results have not been measured.
|
||||
- [Per-face depth bias](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver): Vulkan has one depth bias per draw, so a draw that rasterizes both faces with different front and back bias (PA_SU_SC_MODE_CNTL, PA_SU_POLY_OFFSET_FRONT/BACK_*) is recorded twice, front faces with the back faces culled and then back faces with the front faces culled. The vertex stages run twice, so a vertex shader that writes memory writes twice, and all front faces land before all back faces instead of in primitive order, which differs only for blending or stencil updates that depend on the order.
|
||||
- [Partial rect-list primitives](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver): a rect-list draw whose vertex or index count is not a multiple of 3 draws its complete rectangles and leaves out the 1 or 2 vertices after them, because rect lists are drawn as Vulkan patch lists of 3 control points, which generate [⌊vertexCount/3⌋ patches](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#drawing-patch-lists). What the console draws for the incomplete rectangle is not measured; Palworld (PPSA23126) draws 4 vertices × 9 instances as a rect list when its character creation opens.
|
||||
- [Separate alpha blend into 8_8 SWAP_ALT targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp) (libSceAgcDriver): the second channel of a two-channel target with `COMP_SWAP` 1 holds the alpha export, but Vulkan blends it with the color equation of the R8G8 attachment, so a draw whose alpha equation differs from its color equation is recorded twice, the first channel with the color equation and the second with the alpha factors and operation moved into the color equation. The vertex stages run twice, so a vertex shader that writes memory writes twice, and occlusion counts include both passes. Draws that write depth or stencil, pixel shaders that write memory and factors of the destination alpha throw. That the hardware blends the second channel with the alpha equation is taken from Mesa's `si_translate_colorswap` and radeonsi's use of it for luminance-alpha formats, not measured on hardware.
|
||||
|
|
|
|||
Loading…
Reference in New Issue