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
13 changes: 12 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,17 @@ struct VertexFormat {
const char* scalar;
};

inline bool NullVertexDescriptor(const ShaderRecompiler::VertexAttribute& attribute) {
const auto& fields = attribute.resource.fields;
return fields[0] == 0 && fields[1] == 0 && fields[2] == 0 && fields[3] == 0;
}

inline VertexFormat DecodeVertexFormat(const ShaderRecompiler::VertexAttribute& attribute) {
Require(attribute.components >= 1 && attribute.components <= 4, "invalid vertex attribute component count");
if (NullVertexDescriptor(attribute)) {
const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT};
return {formats[attribute.components - 1], attribute.components * 4u, 4u, "f32"};
}
const auto components = attribute.formatComponents == 0u ? attribute.components : attribute.formatComponents;
Require(components >= 1u && components <= 4u, "invalid vertex format component count");
const auto format = (attribute.resource.fields[3] >> 12u) & 0x7fu;
Expand Down Expand Up @@ -110,7 +119,7 @@ inline VertexInputLayout BuildVertexInputLayout(const Context& context, std::spa
Require((fields[1] & 0x80000000u) == 0 && (fields[3] & 0x00800000u) == 0 && (fields[3] >> 30u) == 0, "unsupported vertex buffer descriptor flags");
const auto stride = (fields[1] >> 16u) & 0x3fffu;
const auto address = fields[0] | (static_cast<std::uint64_t>(fields[1] & 0xffffu) << 32u);
Require(address != 0 && address % format.alignment == 0 && stride % format.alignment == 0, "unaligned vertex buffer");
Require(NullVertexDescriptor(attribute) || (address != 0 && address % format.alignment == 0 && stride % format.alignment == 0), "unaligned vertex buffer");
Require(stride <= context.limits.maxVertexInputBindingStride, "vertex stride exceeds device limits");
Require(context.formatProperties != nullptr, "missing vertex format property query");
VkFormatProperties properties{};
Expand Down Expand Up @@ -142,6 +151,7 @@ inline std::uint64_t ZeroStrideAvailableBytes(const ShaderRecompiler::VertexAttr
}

inline std::size_t VertexBufferExtent(const ShaderRecompiler::VertexAttribute& attribute) {
if (NullVertexDescriptor(attribute)) return DecodeVertexFormat(attribute).bytes;
const auto stride = (attribute.resource.fields[1] >> 16u) & 0x3fffu;
const auto records = attribute.resource.fields[2];
Require(attribute.fetchIndex <= 1, "unsupported vertex fetch index");
Expand All @@ -155,6 +165,7 @@ inline std::size_t VertexBufferExtent(const ShaderRecompiler::VertexAttribute& a
inline std::size_t VertexBufferReadSize(const ShaderRecompiler::VertexAttribute& attribute, std::uint32_t maxIndex, std::uint32_t instances, std::uint32_t firstInstance = 0) {
Require(instances != 0, "vertex input requires nonzero instance count");
Require(firstInstance <= std::numeric_limits<std::uint32_t>::max() - (instances - 1u), "vertex input instance range overflow");
if (NullVertexDescriptor(attribute)) return DecodeVertexFormat(attribute).bytes;
const auto stride = (attribute.resource.fields[1] >> 16u) & 0x3fffu;
const auto records = attribute.resource.fields[2];
Require(attribute.fetchIndex <= 1, "unsupported vertex fetch index");
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,7 @@ DrawInputs prepareDrawInputs(const Context& context, const State& state, const P
std::vector<std::shared_ptr<Buffer>> zeroed(attributes.size());
for (std::size_t i = 0; i < attributes.size(); ++i) {
const auto& attribute = attributes[i];
if (VertexFetchOutOfRange(attribute)) {
if (NullVertexDescriptor(attribute) || VertexFetchOutOfRange(attribute)) {
zeroed[i] = zeroVertexBuffer(context, attribute);
continue;
}
Expand Down
16 changes: 16 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3479,6 +3479,21 @@ void storeAtFlipTests() {
for (const char* value : {"0", "", "true", "11"}) expectFailure([&] { StorageTexture::StoreAtFlipRequested(value); }, "expected 1");
}

void nullVertexDescriptorTests() {
using AgcDriver::Graphics::DecodeVertexFormat;
constexpr std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT};
ShaderRecompiler::VertexAttribute attribute{};
attribute.formatComponents = 1;
for (std::uint32_t components = 1; components <= 4; ++components) {
attribute.components = components;
const auto format = DecodeVertexFormat(attribute);
Require(format.format == formats[components - 1] && format.bytes == components * 4u && std::string_view(format.scalar) == "f32", "a null V# did not read as zero floats of the attribute's width");
Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 100000, 3, 2) == format.bytes && AgcDriver::Graphics::VertexBufferExtent(attribute) == format.bytes, "a null V# was sized as a guest range");
}
attribute.resource.fields = {0x1000, 0, 0, 0};
expectFailure([&] { DecodeVertexFormat(attribute); }, "unsupported vertex format");
}

void vertexCopyTests() {
using AgcDriver::Graphics::PlanVertexCopies;
using AgcDriver::Graphics::VertexFetch;
Expand Down Expand Up @@ -3605,6 +3620,7 @@ int main() {
vertexCopyTests();
highestDrawIndexTests();
storeAtFlipTests();
nullVertexDescriptorTests();
pixelParameterSlotTests();
rectListTests();
floatControlsModeTests();
Expand Down
1 change: 1 addition & 0 deletions docs/dev/TechnicalDebt.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,4 @@ Throughout the project, every function at every stage either **does exactly what
- [syslog](../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER`, and errno is preserved. `openlog`, `setlogmask` and `closelog` are not implemented, so the identity, options and mask they set are not applied
- [sigaction](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do; their mask and flags are not applied. `SA_SIGINFO` handlers and other signals (`SIGBUS`, `SIGHUP`, ...) are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`; `raise` of an `SA_SIGINFO` handler on a signal `signal` delivers throws instead of terminating, and `raise` of the other signals still returns `EINVAL`
- [AJM](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp), [APR](../../core/libs/prx/libkernel/Apr/src/Apr.cpp) and [NGS2](../../core/libs/prx/libSceNgs2.native/src/System.cpp) state is allocated with `new` and never freed on purpose: title threads still call into these libraries while `std::exit` runs static destructors, so ordinary statics would be destroyed under them. Keep them leaked; `guest_ajm_teardown` covers the AJM case on Unix.
- [Null vertex buffer V#](../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a V# whose four dwords are all zero is bound to a zeroed vertex buffer of the attribute's size with stride 0, so every component of its attribute reads 0. This follows the V# fields in the [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) (`NUM_RECORDS` 0, `DST_SEL` 0 selects 0), not measured on hardware.
Loading