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
@@ -0,0 +1,96 @@
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include "Recompiler.hpp"
#include "VulkanTestDevice.hpp"
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>

namespace {

using AgcDriver::Graphics::Require;
using ShaderRecompiler::ShaderStage;

constexpr std::size_t BlockBytes = 65536;

alignas(256) constexpr std::array<std::uint32_t, 25> CrossWorkgroupFlagCode{
0xbe840380, 0x7e000280, 0xbf068002, 0xbf840004, 0x7e020281, 0xdc718000, 0x00000100, 0xbf810000,
0xdc319000, 0x01000000, 0xbf8c3f70, 0x7d840280, 0x80048104, 0xbf0aff04, 0x000f4240, 0xbf840005,
0xbf87fff7, 0x7e020282, 0xdc718004, 0x00000100, 0xbf810000, 0x7e020283, 0xdc718004, 0x00000100,
0xbf810000,
};

class GuestBlock {
public:
GuestBlock() {
#ifdef _WIN32
block = static_cast<std::uint8_t*>(VirtualAlloc(nullptr, BlockBytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
#else
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "cross-workgroup flag: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() { GuestAllocations::Mutation().Remove(block); }
GuestBlock(const GuestBlock&) = delete;
GuestBlock& operator=(const GuestBlock&) = delete;
std::uint8_t* Data() { return block; }

private:
std::uint8_t* block = nullptr;
};

std::uint32_t Run(AgcDriver::VulkanDevice& device, GuestBlock& guest, std::uint32_t waveSize, std::uint32_t preset) {
std::memset(guest.Data(), 0, BlockBytes);
std::memcpy(guest.Data(), &preset, sizeof(preset));
const auto address = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(guest.Data()));
const std::vector<std::uint32_t> userData{static_cast<std::uint32_t>(address), static_cast<std::uint32_t>(address >> 32u)};
const std::span<const std::uint32_t> code(CrossWorkgroupFlagCode);
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
const ShaderRecompiler::ShaderComputeStageInfo compute{{64, 1, 1}, 0, {true, false, false}, false, 1};
ShaderRecompiler::RecompileRequest request{
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
{waveSize, 0, userData, compute, std::nullopt, std::nullopt, memory},
device.Target(),
{0, 0, 0, 128}
};
request.useCache = false;
const auto result = ShaderRecompiler::Recompile(request);
device.Dispatch(result, 2, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
device.WaitIdle();
std::uint32_t words[2]{};
std::memcpy(words, guest.Data(), sizeof(words));
std::printf("wave%u: flag %u, result %u\n", waveSize, words[0], words[1]);
return words[1];
}

}

int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestBlock guest;
Require(Run(*device, guest, 64, 1) == 2u && Run(*device, guest, 32, 1) == 2u, "a workgroup did not see a flag the CPU stored before the dispatch");
const auto wave64 = Run(*device, guest, 64, 0);
const auto wave32 = Run(*device, guest, 32, 0);
Require(wave64 == 2u && wave32 == 2u, "a workgroup did not see another workgroup's flag store (2 = seen, 3 = gave up)");
std::puts("cross-workgroup flag tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}
33 changes: 31 additions & 2 deletions core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ std::uint32_t DefineBdaSpanReadFunction(SpirvEmitterState& state, bool coherent)
const auto pointer = state.module.AllocateId();
state.module.AddFunction(spv::OpConvertUToPtr, TypePhysicalU32Pointer(state), pointer, Binary(state, spv::OpIAdd, u64, physical, delta));
const auto value = state.module.AllocateId();
state.module.AddFunction(spv::OpLoad, u32, value, pointer, BdaAccessMask(coherent), 4u);
if (coherent) state.module.AddFunction(spv::OpAtomicLoad, u32, value, pointer, ConstantU32(state, spv::ScopeDevice), ConstantU32(state, 0u));
else state.module.AddFunction(spv::OpLoad, u32, value, pointer, BdaAccessMask(coherent), 4u);
return value;
});
}
Expand Down Expand Up @@ -234,7 +235,35 @@ void DefineBdaDwordReadFunctions(SpirvEmitterState& state) {
EmitLabel(state, state.module.AllocateId());
state.bdaStopsInvocations = stop;
EmitBdaOverflowCheck(state, address, 4u, instruction);
const auto value = EmitBdaBytes(state, address, 4u, instruction, BdaAccessMask(coherent));
std::uint32_t value = 0;
if (coherent && state.bdaProbeFunction != 0) {
const auto u64t = TypeScalarU64(state);
const auto physical = state.module.AllocateId();
state.module.AddFunction(spv::OpFunctionCall, u64t, physical, state.bdaProbeFunction, address, ConstantU32(state, 4u), instruction);
const auto usable = Binary(state, spv::OpLogicalAnd, TypeBool(state), Binary(state, spv::OpINotEqual, TypeBool(state), physical, BdaConstant(state, 0u)), Binary(state, spv::OpIEqual, TypeBool(state), Binary(state, spv::OpBitwiseAnd, u64t, physical, BdaConstant(state, 3u)), BdaConstant(state, 0u)));
const auto atomicLabel = state.module.AllocateId();
const auto byteLabel = state.module.AllocateId();
const auto byteExit = state.module.AllocateId();
const auto merge = state.module.AllocateId();
state.module.AddFunction(spv::OpSelectionMerge, merge, spv::SelectionControlMaskNone);
state.module.AddFunction(spv::OpBranchConditional, usable, atomicLabel, byteLabel);
EmitLabel(state, atomicLabel);
const auto pointer = state.module.AllocateId();
state.module.AddFunction(spv::OpConvertUToPtr, TypePhysicalU32Pointer(state), pointer, physical);
const auto atomicValue = state.module.AllocateId();
state.module.AddFunction(spv::OpAtomicLoad, u32, atomicValue, pointer, ConstantU32(state, spv::ScopeDevice), ConstantU32(state, 0u));
state.module.AddFunction(spv::OpBranch, merge);
EmitLabel(state, byteLabel);
const auto byteValue = EmitBdaBytes(state, address, 4u, instruction, BdaAccessMask(coherent));
state.module.AddFunction(spv::OpBranch, byteExit);
EmitLabel(state, byteExit);
state.module.AddFunction(spv::OpBranch, merge);
EmitLabel(state, merge);
value = state.module.AllocateId();
state.module.AddFunction(spv::OpPhi, u32, value, atomicValue, atomicLabel, byteValue, byteExit);
} else {
value = EmitBdaBytes(state, address, 4u, instruction, BdaAccessMask(coherent));
}
const auto result = state.module.AllocateId();
state.module.AddFunction(spv::OpCompositeConstruct, pair, result, value, ConstantU32(state, 0u));
state.module.AddFunction(spv::OpReturnValue, result);
Expand Down
Loading