Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,11 @@ std::shared_ptr<Texture> cachedTexture(const Context& context, std::span<const s
return depth;
}
const auto depthBitsWidth = words.size() >= 4 ? ShaderRecompiler::DepthBitsTextureWidth(words[1], words[3]) : 0u;
if (depthBitsWidth == 32u) {
char text[160];
std::snprintf(text, sizeof(text), "AGC graphics: 32-bit integer read of the depth-layout texture 0x%llx, which is no depth surface drawn with, is not implemented", static_cast<unsigned long long>(resource.baseAddress));
throw std::runtime_error(text);
constexpr auto float32 = static_cast<std::uint32_t>(ShaderRecompiler::IrBufferFormat::Format32Float);
if (depthBitsWidth == 32u && resource.format != float32) {
auto bits = resource;
bits.format = float32;
return cachedTexture(context, words, bits, components, guestBytes, depthCompare);
}
constexpr auto unorm16 = static_cast<std::uint32_t>(ShaderRecompiler::IrBufferFormat::Format16UNorm);
if (depthBitsWidth == 16u && resource.format != unorm16) {
Expand Down
51 changes: 51 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/SpirvLdsAtomicLock.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Recompiler.hpp"
#include <spirv/unified1/spirv.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
Expand Down Expand Up @@ -28,6 +29,7 @@ struct Usage {
std::size_t releases = 0;
std::size_t adds = 0;
bool addBetweenLock = false;
bool lockInitIsLaneZero = false;
};

void Require(bool condition, const std::string& message) {
Expand Down Expand Up @@ -63,14 +65,32 @@ std::vector<std::uint32_t> RecompileProgram(std::span<const std::uint32_t> code)
Usage Scan(std::span<const std::uint32_t> words) {
Usage usage;
bool locked = false;
std::uint32_t localIndexVariable = 0u;
std::uint32_t localIndexLoad = 0u;
std::uint32_t u32Type = 0u;
std::uint32_t zero = 0u;
std::uint32_t laneZeroCondition = 0u;
std::uint32_t laneZeroBody = 0u;
std::uint32_t lockPointer = 0u;
for (std::size_t cursor = 5; cursor < words.size();) {
const auto count = words[cursor] >> spv::WordCountShift;
const auto op = static_cast<spv::Op>(words[cursor] & spv::OpCodeMask);
Require(count != 0 && cursor + count <= words.size(), "truncated SPIR-V instruction");
if (op == spv::OpDecorate && count >= 4u && words[cursor + 2u] == spv::DecorationBuiltIn && words[cursor + 3u] == spv::BuiltInLocalInvocationIndex) {
localIndexVariable = words[cursor + 1u];
}
if (op == spv::OpTypeInt && count >= 4u && words[cursor + 2u] == 32u && words[cursor + 3u] == 0u) u32Type = words[cursor + 1u];
if (op == spv::OpConstant && count >= 4u && words[cursor + 1u] == u32Type && words[cursor + 3u] == 0u) zero = words[cursor + 2u];
if (op == spv::OpLoad && count >= 4u && words[cursor + 3u] == localIndexVariable) localIndexLoad = words[cursor + 2u];
if (op == spv::OpIEqual && count >= 5u && ((words[cursor + 3u] == localIndexLoad && words[cursor + 4u] == zero) || (words[cursor + 4u] == localIndexLoad && words[cursor + 3u] == zero))) {
laneZeroCondition = words[cursor + 2u];
}
if (op == spv::OpBranchConditional && count >= 4u && words[cursor + 1u] == laneZeroCondition) laneZeroBody = words[cursor + 2u];
if (op == spv::OpGroupNonUniformElect) ++usage.elects;
if (op == spv::OpAtomicCompareExchange) {
++usage.exchanges;
locked = true;
if (count >= 4u) lockPointer = words[cursor + 3u];
}
if (op == spv::OpAtomicStore) {
++usage.releases;
Expand All @@ -82,6 +102,36 @@ Usage Scan(std::span<const std::uint32_t> words) {
}
cursor += count;
}
std::uint32_t lockBase = 0u;
std::uint32_t lockIndex = 0u;
for (std::size_t cursor = 5; cursor < words.size();) {
const auto count = words[cursor] >> spv::WordCountShift;
const auto op = static_cast<spv::Op>(words[cursor] & spv::OpCodeMask);
if (op == spv::OpAccessChain && count >= 5u && words[cursor + 2u] == lockPointer) {
lockBase = words[cursor + 3u];
lockIndex = words[cursor + 4u];
break;
}
cursor += count;
}
std::vector<std::uint32_t> lockPointers{lockPointer};
for (std::size_t cursor = 5; cursor < words.size();) {
const auto count = words[cursor] >> spv::WordCountShift;
const auto op = static_cast<spv::Op>(words[cursor] & spv::OpCodeMask);
if (op == spv::OpAccessChain && count >= 5u && words[cursor + 3u] == lockBase && words[cursor + 4u] == lockIndex) {
lockPointers.push_back(words[cursor + 2u]);
}
cursor += count;
}
bool inLaneZeroBody = false;
for (std::size_t cursor = 5; cursor < words.size();) {
const auto count = words[cursor] >> spv::WordCountShift;
const auto op = static_cast<spv::Op>(words[cursor] & spv::OpCodeMask);
if (op == spv::OpLabel) inLaneZeroBody = words[cursor + 1u] == laneZeroBody;
if (inLaneZeroBody && op == spv::OpStore && count >= 3u && std::find(lockPointers.begin(), lockPointers.end(), words[cursor + 1u]) != lockPointers.end()) usage.lockInitIsLaneZero = true;
if (inLaneZeroBody && (op == spv::OpBranch || op == spv::OpBranchConditional || op == spv::OpReturn)) inLaneZeroBody = false;
cursor += count;
}
return usage;
}

Expand All @@ -95,6 +145,7 @@ Usage Compile(std::initializer_list<std::uint32_t> body) {
void Check64BitOnlyLocks() {
const auto usage = Compile({DsAddU64, DsOperands});
Require(usage.elects == 1 && usage.exchanges == 1 && usage.releases == 1, "a ds_add_u64 is not serialised by one elected lane taking the workgroup lock");
Require(usage.lockInitIsLaneZero, "the workgroup lock is not initialized by only local invocation zero");
}

void Check32BitTakesLockWith64Bit() {
Expand Down
55 changes: 40 additions & 15 deletions core/libs/prx/libSceAgcDriver/tests/execution/VopcCompareF64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstdio>
#include <iostream>
#include <limits>
#include <optional>
#include <string>
#include <vector>

Expand All @@ -22,6 +23,11 @@ constexpr std::uint32_t Inputs = 8;
constexpr std::uint32_t Results = 4;
constexpr std::uint32_t Compares = 41;
constexpr std::uint32_t RandomRounds = 4;
struct Mode {
const char* name;
std::optional<ShaderRecompiler::ShaderFloatMode> value;
bool flushed;
};
alignas(256) std::array<std::uint32_t, Threads * Inputs> Input{};
alignas(256) std::array<std::uint32_t, Threads * Results> Output{};

Expand Down Expand Up @@ -62,11 +68,12 @@ alignas(256) constexpr std::array<std::uint32_t, 271> CompareCode{
0xd76f000b, 0x042d1114, 0xe0702000, 0x80010a03, 0xe0702004, 0x80010b03, 0xbf810000,
};

constexpr std::array<std::uint64_t, 20> Edges{
constexpr std::array<std::uint64_t, 22> Edges{
0x7ff8000000000000ull, 0x7ff0000000000001ull, 0xfff8000000000001ull, 0x7ff0000100000000ull, 0x7ff0000000000000ull,
0xfff0000000000000ull, 0x0000000000000000ull, 0x8000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull,
0x800fffffffffffffull, 0x0010000000000000ull, 0x3ff0000000000000ull, 0x3ff0000000000001ull, 0xbff0000000000000ull,
0x4008000000000000ull, 0xc008000000000000ull, 0x3fc45f306dc9c882ull, 0x3fc45f3060000000ull, 0x7fefffffffffffffull,
0x8000000000000001ull, 0x000fffffffffffffull,
};

constexpr std::array<std::uint32_t, 16> Masks{
Expand Down Expand Up @@ -113,8 +120,7 @@ std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t co
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (4u << 16u), count, 0x11016facu};
}

void Run(AgcDriver::VulkanDevice& device) {
Output.fill(0xdeadbeefu);
ShaderRecompiler::RecompileResult Compile(AgcDriver::VulkanDevice& device, const Mode& mode) {
std::vector<std::uint32_t> userData(8, 0u);
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size()));
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size()));
Expand All @@ -130,9 +136,8 @@ void Run(AgcDriver::VulkanDevice& device) {
{0, 0, 0, 128}
};
request.useCache = false;
const auto result = ShaderRecompiler::Recompile(request);
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
device.WaitIdle();
request.context.floatMode = mode.value;
return ShaderRecompiler::Recompile(request);
}

std::uint32_t ClassBit(std::uint64_t bits) {
Expand All @@ -150,14 +155,18 @@ bool ClassMatch(std::uint64_t bits, std::uint32_t mask) {
return ((mask >> ClassBit(bits)) & 1u) != 0u;
}

void Check(std::uint32_t round) {
void Check(std::uint32_t round, const Mode& mode) {
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
const auto* in = &Input[tid * Inputs];
const std::uint64_t abits = in[0] | (static_cast<std::uint64_t>(in[1]) << 32u);
const std::uint64_t bbits = in[2] | (static_cast<std::uint64_t>(in[3]) << 32u);
const std::uint32_t mask = in[4];
const double a = std::bit_cast<double>(abits);
const double b = std::bit_cast<double>(bbits);
const auto value = [&](std::uint64_t bits) {
const double result = std::bit_cast<double>(bits);
return mode.flushed && std::fpclassify(result) == FP_SUBNORMAL ? std::copysign(0.0, result) : result;
};
const double a = value(abits);
const double b = value(bbits);
const bool unordered = std::isnan(a) || std::isnan(b);
const std::array<bool, 17> base{
false,
Expand Down Expand Up @@ -186,13 +195,13 @@ void Check(std::uint32_t round) {
expected[34] = 3.0 < a;
expected[35] = 1.0 >= a;
expected[36] = std::bit_cast<double>(0x3fc45f306dc9c882ull) == a;
expected[37] = std::numeric_limits<double>::denorm_min() < a;
expected[37] = (mode.flushed ? 0.0 : std::numeric_limits<double>::denorm_min()) < a;
expected[38] = std::fabs(a) < b;
expected[39] = ClassMatch(abits & 0x7fffffffffffffffull, mask);
expected[40] = !(std::fabs(a) > std::fabs(b));
for (std::uint32_t k = 0; k < expected.size(); ++k) {
const bool actual = ((Output[tid * Results + k / 32u] >> (k % 32u)) & 1u) != 0u;
Require(actual == expected[k], "vopc f64 compares: round " + std::to_string(round) + " thread " + std::to_string(tid) + " compare " + std::to_string(k) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected[k]));
Require(actual == expected[k], "vopc f64 compares: " + std::string(mode.name) + " round " + std::to_string(round) + " thread " + std::to_string(tid) + " compare " + std::to_string(k) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected[k]));
}
}
}
Expand All @@ -203,10 +212,26 @@ int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
for (std::uint32_t round = 0; round < EdgeRounds + RandomRounds; ++round) {
FillInput(round);
Run(*device);
Check(round);
const std::array<Mode, 9> modes{{
{"unknown mode", std::nullopt, false},
{"mode 0, IEEE off", ShaderRecompiler::ShaderFloatMode{0x30u, true, false, false}, true},
{"mode 1, IEEE off", ShaderRecompiler::ShaderFloatMode{0x60u, true, false, false}, false},
{"mode 2, IEEE off", ShaderRecompiler::ShaderFloatMode{0x90u, true, false, false}, true},
{"mode 3, IEEE off", ShaderRecompiler::ShaderFloatMode{0xc0u, true, false, false}, false},
{"mode 0, IEEE on", ShaderRecompiler::ShaderFloatMode{0x30u, true, true, false}, true},
{"mode 1, IEEE on", ShaderRecompiler::ShaderFloatMode{0x60u, true, true, false}, false},
{"mode 2, IEEE on", ShaderRecompiler::ShaderFloatMode{0x90u, true, true, false}, true},
{"mode 3, IEEE on", ShaderRecompiler::ShaderFloatMode{0xc0u, true, true, false}, false},
}};
for (const auto& mode : modes) {
const auto result = Compile(*device, mode);
for (std::uint32_t round = 0; round < EdgeRounds + RandomRounds; ++round) {
FillInput(round);
Output.fill(0xdeadbeefu);
device->Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(CompareCode.data()));
device->WaitIdle();
Check(round, mode);
}
}
std::puts("vopc f64 compare tests passed");
return 0;
Expand Down
10 changes: 6 additions & 4 deletions core/libs/prx/libSceAudioOut/src/AudioOut2SpeakerArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,15 @@ size_t APS5_VABI sceAudioOut2GetSpeakerArrayMemorySize(uint32_t num_speakers, ui
int APS5_VABI sceAudioOut2GetSpeakerInfo(AudioOut2SpeakerInfo* info, uint32_t flags) {
(void)flags;
if (!info) return static_cast<int>(0x80260502);
constexpr std::uint8_t SpeakerTypeStereo = 0;
constexpr std::uint8_t SpeakerTypeTelevision = 0;
constexpr std::uint32_t FrontLeftAndRight = 0x3;
constexpr AudioOut2SpeakerAngle NoSpeaker = {-360, -360};
*info = AudioOut2SpeakerInfo{};
info->type = SpeakerTypeStereo;
info->type = SpeakerTypeTelevision;
info->available_bits = FrontLeftAndRight;
info->speaker_angle[0] = {-30, 0};
info->speaker_angle[1] = {30, 0};
for (auto& angle : info->speaker_angle) angle = NoSpeaker;
info->speaker_angle[0] = {30, 0};
info->speaker_angle[1] = {-30, 0};
return 0;
}

Expand Down
6 changes: 3 additions & 3 deletions core/libs/prx/libSceAudioOut/tests/AudioOut2DeviceState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ void SpeakerInfoReportsTheFrontPair() {
Require(info.type == 0);
Require(info.available_bits == 0x3);
Require(info.flags == 0);
Require(info.speaker_angle[0].azimuth == -30 && info.speaker_angle[0].elevation == 0);
Require(info.speaker_angle[1].azimuth == 30 && info.speaker_angle[1].elevation == 0);
for (int index = 2; index < 16; index++) Require(info.speaker_angle[index].azimuth == 0 && info.speaker_angle[index].elevation == 0);
Require(info.speaker_angle[0].azimuth == 30 && info.speaker_angle[0].elevation == 0);
Require(info.speaker_angle[1].azimuth == -30 && info.speaker_angle[1].elevation == 0);
for (int index = 2; index < 16; index++) Require(info.speaker_angle[index].azimuth == -360 && info.speaker_angle[index].elevation == -360);
Require(sceAudioOut2GetSpeakerInfo(nullptr, 0) == errorInvalidArgument);
}

Expand Down
36 changes: 28 additions & 8 deletions core/libs/prx/libSceLibcInternal/Getopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ constexpr int OptionalArgument = 2;

constexpr int FlagPermute = 1;
constexpr int FlagAllArguments = 2;
constexpr int FlagLongOnly = 4;

constexpr int BadCharacter = '?';
constexpr int InOrder = 1;
Expand Down Expand Up @@ -112,7 +113,7 @@ void PermuteArguments(int nonOptionStart, int nonOptionEnd, int optionEnd, char*
}
}

int ParseLongOptions(char* const* arguments, const char* options, const GuestOption* longOptions, int* index, const char* dash) {
int ParseLongOptions(char* const* arguments, const char* options, const GuestOption* longOptions, int* index, const char* dash, bool shortToo, int flags) {
char* const current = g_longPlace;
int match = -1;
bool exactMatch = false;
Expand All @@ -136,8 +137,9 @@ int ParseLongOptions(char* const* arguments, const char* options, const GuestOpt
exactMatch = true;
break;
}
if (shortToo && length == 1) continue;
if (match == -1) match = i;
else if (longOptions[i].hasArgument != longOptions[match].hasArgument ||
else if ((flags & FlagLongOnly) || longOptions[i].hasArgument != longOptions[match].hasArgument ||
longOptions[i].flag != longOptions[match].flag ||
longOptions[i].value != longOptions[match].value)
secondPartialMatch = true;
Expand All @@ -149,6 +151,10 @@ int ParseLongOptions(char* const* arguments, const char* options, const GuestOpt
return BadCharacter;
}
if (match == -1) {
if (shortToo) {
--optind_nid_postfix;
return -1;
}
if (PrintError(options)) Warn(std::string("unrecognized option `") + dash + current + "'");
optopt_nid_postfix = 0;
return BadCharacter;
Expand Down Expand Up @@ -237,11 +243,21 @@ int GetoptInternal(int count, char* const* arguments, const char* options, const
break;
}

if (longOptions && *g_longPlace == '-' && g_longPlace != arguments[optind_nid_postfix]) {
++g_longPlace;
const int result = ParseLongOptions(arguments, options, longOptions, index, "--");
g_longPlace = g_emptyMessage;
return result;
if (longOptions && g_longPlace != arguments[optind_nid_postfix] &&
(*g_longPlace == '-' || (flags & FlagLongOnly))) {
bool shortToo = false;
const char* dash = "-";
if (*g_longPlace == '-') {
++g_longPlace;
dash = "--";
} else if (*g_longPlace != ':' && std::strchr(options, *g_longPlace)) {
shortToo = true;
}
const int result = ParseLongOptions(arguments, options, longOptions, index, dash, shortToo, flags);
if (result != -1) {
g_longPlace = g_emptyMessage;
return result;
}
}

const int character = OptionCharacter(g_longPlace++);
Expand All @@ -265,7 +281,7 @@ int GetoptInternal(int count, char* const* arguments, const char* options, const
}
g_longPlace = Argument(arguments, optind_nid_postfix, "getopt_long");
}
const int result = ParseLongOptions(arguments, options, longOptions, index, "-W ");
const int result = ParseLongOptions(arguments, options, longOptions, index, "-W ", false, flags);
g_longPlace = g_emptyMessage;
return result;
}
Expand Down Expand Up @@ -355,4 +371,8 @@ int APS5_VABI getopt_long_nid_postfix(int count, char* const* arguments, const c
return GetoptInternal(count, arguments, options, longOptions, index, FlagPermute);
}

int APS5_VABI getopt_long_only_nid_postfix(int count, char* const* arguments, const char* options, const GuestOption* longOptions, int* index) {
return GetoptInternal(count, arguments, options, longOptions, index, FlagPermute | FlagLongOnly);
}

}
Loading