mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
perf(recompiler): keep the vector window's unindexed slots out of the array
A shader with any v_movreld kept its whole relative-addressing window (167 registers in a PPSA08427 compute shader) in one Function-storage array, which the device puts in local memory. The window array now holds only the slots the relative accesses likely reach (from a bound analysis of their indices); every other slot is a variable of its own the device can keep in a register. A relative access whose index lands past the array at run time switches over those variables, so the guess only affects speed. APS5_NO_WINDOW_NARROWING=1 turns it off. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
This commit is contained in:
1 parent
43349d4337
commit
30fab0777e
5 files changed
+380
-9
No files matched your search
@@ -0,0 +1,120 @@
|
||||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "Recompiler.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
using ShaderRecompiler::ShaderStage;
|
||||
|
||||
constexpr std::uint32_t MaxThreads = 64;
|
||||
constexpr std::uint32_t Results = 16;
|
||||
constexpr std::uint32_t Checked = 6;
|
||||
alignas(256) std::array<std::uint32_t, MaxThreads * Results> Output{};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 54> Code{
|
||||
0x34020086, 0x34060088, 0x4a140680, 0x4a160681, 0x4a180682, 0x4a1a0683, 0x4a1c0684, 0x4a1e0685,
|
||||
0x4a200686, 0x4a220687, 0x4a240688, 0x4a260689, 0x4a28068a, 0x4a2a068b, 0x4a2c068c, 0x4a2e068d,
|
||||
0x4a30068e, 0x4a32068f, 0x4a340690, 0x4a360691, 0x4a380692, 0x4a3a0693, 0x4a3c0694, 0x4a3e0695,
|
||||
0x4a400696, 0x4a420697, 0x4a440698, 0x4a460699, 0x4a48069a, 0x4a4a069b, 0x4a4c069c, 0x4a4e069d,
|
||||
0x4a50069e, 0x4a52069f, 0x877c8308, 0x7e08870a, 0xbefc0309, 0x7e0a870a, 0x7e148504, 0x877c8308,
|
||||
0x7e148505, 0xe0701000, 0x80010401, 0xe0701004, 0x80010501, 0xe0701008, 0x80010c01, 0xe070100c,
|
||||
0x80010d01, 0xe0701010, 0x80011e01, 0xe0701014, 0x80012901, 0xbf810000,
|
||||
};
|
||||
|
||||
constexpr const char* Names[Checked] = {"v4", "v5", "v12", "v13", "v30", "v41"};
|
||||
|
||||
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
|
||||
}
|
||||
|
||||
std::string Hex(std::uint32_t value) {
|
||||
char text[16];
|
||||
std::snprintf(text, sizeof(text), "0x%08x", value);
|
||||
return text;
|
||||
}
|
||||
|
||||
std::uint32_t FunctionVariables(std::span<const std::uint32_t> words) {
|
||||
std::uint32_t count = 0;
|
||||
for (std::size_t at = 5; at < words.size();) {
|
||||
const auto length = words[at] >> 16u;
|
||||
if (length == 0u) break;
|
||||
if ((words[at] & 0xffffu) == spv::OpVariable && at + 3u < words.size() && words[at + 3u] == spv::StorageClassFunction) ++count;
|
||||
at += length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void Run(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, std::uint32_t threads, std::uint32_t bounded, std::uint32_t data) {
|
||||
Output.fill(0xdeadbeefu);
|
||||
std::vector<std::uint32_t> userData(10, 0u);
|
||||
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
|
||||
std::copy(output.begin(), output.end(), userData.begin() + 4);
|
||||
userData[8] = bounded;
|
||||
userData[9] = data;
|
||||
const std::span<const std::uint32_t> code(Code);
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{threads, 1, 1}, 0u, {false, 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);
|
||||
Require(FunctionVariables(result.spirv.Words()) > 8u, "vector window narrowing: wave" + std::to_string(waveSize) + " window was not split into separate slot variables");
|
||||
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
|
||||
device.WaitIdle();
|
||||
}
|
||||
|
||||
void Check(std::uint32_t waveSize, std::uint32_t threads, std::uint32_t bounded, std::uint32_t data) {
|
||||
for (std::uint32_t tid = 0; tid < threads; ++tid) {
|
||||
std::array<std::uint32_t, 32> window{};
|
||||
for (std::uint32_t k = 0; k < window.size(); ++k) window[k] = (tid << 8u) + k;
|
||||
const std::uint32_t v4 = window[bounded & 3u];
|
||||
const std::uint32_t v5 = window[data];
|
||||
window[data] = v4;
|
||||
window[bounded & 3u] = v5;
|
||||
const std::array<std::uint32_t, Checked> expected{v4, v5, window[2], window[3], window[20], window[31]};
|
||||
const std::uint32_t* out = &Output[tid * Results];
|
||||
for (std::uint32_t index = 0; index < Checked; ++index) {
|
||||
Require(out[index] == expected[index], "vector window narrowing: wave" + std::to_string(waveSize) + " data index " + std::to_string(data) + " lane " + std::to_string(tid) + " " + Names[index] + " is " + Hex(out[index]) + ", expected " + Hex(expected[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
if (device->Target().subgroupSize < 32u) {
|
||||
std::printf("skipped, subgroup size %u cannot hold a wave32\n", device->Target().subgroupSize);
|
||||
return VulkanTestSkipped;
|
||||
}
|
||||
for (const std::uint32_t data : {20u, 2u, 31u}) {
|
||||
Run(*device, 32, 32, 7u, data);
|
||||
Check(32, 32, 7u, data);
|
||||
Run(*device, 64, 64, 7u, data);
|
||||
Check(64, 64, 7u, data);
|
||||
}
|
||||
std::puts("vector window narrowing tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,8 @@ struct SpirvEmitterState {
|
||||
std::uint32_t numWorkgroupsVariable = 0;
|
||||
std::array<std::uint32_t, 2> scratchVariable {};
|
||||
std::array<std::uint32_t, 2> vectorWindowVariable {};
|
||||
std::uint32_t vectorWindowIndexed = 0;
|
||||
std::array<std::vector<std::uint32_t>, 2> vectorWindowSlots {};
|
||||
std::array<std::uint32_t, ImageBindingCount> imageVariables {};
|
||||
std::uint32_t samplerVariable = 0;
|
||||
std::uint32_t mainFunc = 0;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "SpirvBackend/SpirvAluEmitter.hpp"
|
||||
#include "SpirvBackend/SpirvEmitterHelpers.hpp"
|
||||
#include "SpirvBackend/SpirvEmitterInstructions.hpp"
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
@@ -45,21 +48,89 @@ void EmitSetVectorRegister(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
EmitUnreachable(ctx, inst);
|
||||
}
|
||||
|
||||
std::uint32_t VectorWindowElement(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
auto& state = ctx.state;
|
||||
namespace {
|
||||
|
||||
std::uint32_t VectorWindowElement(SpirvEmitterState& state, std::uint32_t index) {
|
||||
const auto pointer = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpAccessChain, TypePointer(state, spv::StorageClassFunction, TypeU32(state)), pointer, state.vectorWindowVariable.at(state.laneHalf), ctx.Arg(inst, 0u));
|
||||
state.module.AddFunction(spv::OpAccessChain, TypePointer(state, spv::StorageClassFunction, TypeU32(state)), pointer, state.vectorWindowVariable.at(state.laneHalf), index);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
std::uint32_t AccessNarrowedWindow(SpirvEmitterState& state, std::uint32_t index, const std::function<std::uint32_t(std::uint32_t)>& access) {
|
||||
const auto& slots = state.vectorWindowSlots.at(state.laneHalf);
|
||||
const auto arrayLabel = state.module.AllocateId();
|
||||
const auto slotsLabel = state.module.AllocateId();
|
||||
const auto slotsMerge = state.module.AllocateId();
|
||||
const auto merge = state.module.AllocateId();
|
||||
const auto inArray = Binary(state, spv::OpULessThan, TypeBool(state), index, ConstantU32(state, state.vectorWindowIndexed));
|
||||
state.module.AddFunction(spv::OpSelectionMerge, merge, spv::SelectionControlMaskNone);
|
||||
state.module.AddFunction(spv::OpBranchConditional, inArray, arrayLabel, slotsLabel);
|
||||
EmitLabel(state, arrayLabel);
|
||||
const auto fromArray = access(VectorWindowElement(state, index));
|
||||
const auto arrayExit = state.currentLabel;
|
||||
state.module.AddFunction(spv::OpBranch, merge);
|
||||
EmitLabel(state, slotsLabel);
|
||||
std::vector<std::uint32_t> labels(slots.size());
|
||||
std::vector<std::uint32_t> words{spv::OpSwitch, index, slotsMerge};
|
||||
for (std::size_t slot = 0; slot < slots.size(); ++slot) {
|
||||
labels[slot] = state.module.AllocateId();
|
||||
words.push_back(state.vectorWindowIndexed + static_cast<std::uint32_t>(slot));
|
||||
words.push_back(labels[slot]);
|
||||
}
|
||||
state.module.AddFunction(spv::OpSelectionMerge, slotsMerge, spv::SelectionControlMaskNone);
|
||||
state.module.AddFunction(std::span<const std::uint32_t>(words));
|
||||
std::vector<std::uint32_t> incoming;
|
||||
for (std::size_t slot = 0; slot < slots.size(); ++slot) {
|
||||
EmitLabel(state, labels[slot]);
|
||||
incoming.push_back(access(slots[slot]));
|
||||
incoming.push_back(labels[slot]);
|
||||
state.module.AddFunction(spv::OpBranch, slotsMerge);
|
||||
}
|
||||
incoming.push_back(ConstantU32(state, 0u));
|
||||
incoming.push_back(slotsLabel);
|
||||
EmitLabel(state, slotsMerge);
|
||||
const auto fromSlots = state.module.AllocateId();
|
||||
std::vector<std::uint32_t> phi{spv::OpPhi, TypeU32(state), fromSlots};
|
||||
phi.insert(phi.end(), incoming.begin(), incoming.end());
|
||||
state.module.AddFunction(std::span<const std::uint32_t>(phi));
|
||||
state.module.AddFunction(spv::OpBranch, merge);
|
||||
EmitLabel(state, merge);
|
||||
const auto value = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpPhi, TypeU32(state), value, fromArray, arrayExit, fromSlots, slotsMerge);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint32_t DirectWindowElement(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
auto& state = ctx.state;
|
||||
const IrValue* index = inst.Argument(0)->Resolve();
|
||||
if (state.vectorWindowIndexed == 0u) return VectorWindowElement(state, ctx.Arg(inst, 0u));
|
||||
if (!index->HasImmediate()) return 0u;
|
||||
if (index->ImmediateU32() < state.vectorWindowIndexed) return VectorWindowElement(state, ctx.Arg(inst, 0u));
|
||||
return state.vectorWindowSlots.at(state.laneHalf).at(index->ImmediateU32() - state.vectorWindowIndexed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EmitLoadVectorWindow(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
const auto value = ctx.state.module.AllocateId();
|
||||
ctx.state.module.AddFunction(spv::OpLoad, TypeU32(ctx.state), value, VectorWindowElement(ctx, inst));
|
||||
ctx.Define(inst, value);
|
||||
auto& state = ctx.state;
|
||||
const auto load = [&state](std::uint32_t pointer) {
|
||||
const auto value = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpLoad, TypeU32(state), value, pointer);
|
||||
return value;
|
||||
};
|
||||
const auto pointer = DirectWindowElement(ctx, inst);
|
||||
ctx.Define(inst, pointer != 0u ? load(pointer) : AccessNarrowedWindow(state, ctx.Arg(inst, 0u), load));
|
||||
}
|
||||
|
||||
void EmitStoreVectorWindow(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
ctx.state.module.AddFunction(spv::OpStore, VectorWindowElement(ctx, inst), ctx.Arg(inst, 1u));
|
||||
auto& state = ctx.state;
|
||||
const auto value = ctx.Arg(inst, 1u);
|
||||
const auto store = [&state, value](std::uint32_t pointer) {
|
||||
state.module.AddFunction(spv::OpStore, pointer, value);
|
||||
return ConstantU32(state, 0u);
|
||||
};
|
||||
if (const auto pointer = DirectWindowElement(ctx, inst); pointer != 0u) (void)store(pointer);
|
||||
else (void)AccessNarrowedWindow(state, ctx.Arg(inst, 0u), store);
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
#include "SpirvBackend/SpirvEmitterHelpers.hpp"
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -41,6 +46,170 @@ std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t ty
|
||||
return variable;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class WindowIndexBounds {
|
||||
public:
|
||||
std::optional<std::uint64_t> Of(const IrValue& input) {
|
||||
const IrValue* value = input.Resolve();
|
||||
if (value->HasImmediate()) return value->ImmediateU32();
|
||||
if (const auto found = memo.find(value); found != memo.end()) return found->second;
|
||||
if (!active.insert(value).second || active.size() > 256u) return std::nullopt;
|
||||
const auto bound = compute(*value);
|
||||
active.erase(value);
|
||||
memo.emplace(value, bound);
|
||||
return bound;
|
||||
}
|
||||
|
||||
private:
|
||||
using Bound = std::optional<std::uint64_t>;
|
||||
|
||||
Bound maxOf(std::initializer_list<Bound> bounds) {
|
||||
std::uint64_t result = 0;
|
||||
for (const auto& bound : bounds) {
|
||||
if (!bound) return std::nullopt;
|
||||
result = std::max(result, *bound);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<const IrValue*> reachingStores(const IrValue& load, std::uint32_t slot) {
|
||||
std::vector<const IrValue*> values;
|
||||
std::unordered_set<const IrBlock*> visited;
|
||||
std::vector<std::pair<IrBlock*, const IrValue*>> pending {{load.Parent(), &load}};
|
||||
while (!pending.empty()) {
|
||||
const auto [block, before] = pending.back();
|
||||
pending.pop_back();
|
||||
const auto& instructions = block->Instructions();
|
||||
auto it = instructions.rbegin();
|
||||
if (before != nullptr) it = std::next(std::find(instructions.rbegin(), instructions.rend(), before));
|
||||
bool stored = false;
|
||||
for (; it != instructions.rend() && !stored; ++it) {
|
||||
if ((*it)->Opcode() != IrOpcode::StoreVectorWindow) continue;
|
||||
const IrValue* index = (*it)->Argument(0)->Resolve();
|
||||
if (!index->HasImmediate() || index->ImmediateU32() != slot) continue;
|
||||
values.push_back((*it)->Argument(1));
|
||||
stored = true;
|
||||
}
|
||||
if (stored) continue;
|
||||
for (IrBlock* predecessor : block->Predecessors()) {
|
||||
if (visited.insert(predecessor).second) pending.emplace_back(predecessor, nullptr);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
Bound compute(const IrValue& value) {
|
||||
const auto arg = [&](std::size_t index) { return Of(*value.Argument(index)); };
|
||||
const auto immediate = [&](std::size_t index) -> std::optional<std::uint32_t> {
|
||||
const IrValue* operand = value.Argument(index)->Resolve();
|
||||
if (!operand->HasImmediate()) return std::nullopt;
|
||||
return operand->ImmediateU32();
|
||||
};
|
||||
const auto fits = [](std::uint64_t bound) -> Bound { return bound <= 0xffffffffull ? Bound(bound) : std::nullopt; };
|
||||
switch (value.Opcode()) {
|
||||
case IrOpcode::BitFieldUExtract: {
|
||||
const auto count = immediate(2);
|
||||
if (count && *count < 32u) return (std::uint64_t {1} << *count) - 1u;
|
||||
return arg(0);
|
||||
}
|
||||
case IrOpcode::BitwiseAnd32: {
|
||||
const auto lhs = arg(0);
|
||||
const auto rhs = arg(1);
|
||||
if (lhs && rhs) return std::min(*lhs, *rhs);
|
||||
return lhs ? lhs : rhs;
|
||||
}
|
||||
case IrOpcode::UMin32: {
|
||||
const auto lhs = arg(0);
|
||||
const auto rhs = arg(1);
|
||||
if (!lhs || !rhs) return std::nullopt;
|
||||
return std::min(*lhs, *rhs);
|
||||
}
|
||||
case IrOpcode::UMax32:
|
||||
return maxOf({arg(0), arg(1)});
|
||||
case IrOpcode::BitwiseOr32: {
|
||||
const auto both = maxOf({arg(0), arg(1)});
|
||||
if (!both) return std::nullopt;
|
||||
std::uint64_t mask = 0;
|
||||
while (mask < *both) mask = mask << 1u | 1u;
|
||||
return mask;
|
||||
}
|
||||
case IrOpcode::IAdd32: {
|
||||
const auto lhs = arg(0);
|
||||
const auto rhs = arg(1);
|
||||
if (!lhs || !rhs) return std::nullopt;
|
||||
return fits(*lhs + *rhs);
|
||||
}
|
||||
case IrOpcode::ShiftRightLogical32: {
|
||||
const auto shift = immediate(1);
|
||||
const auto bound = arg(0);
|
||||
if (!bound) return std::nullopt;
|
||||
return shift ? *bound >> (*shift & 31u) : *bound;
|
||||
}
|
||||
case IrOpcode::ShiftLeftLogical32: {
|
||||
const auto shift = immediate(1);
|
||||
const auto bound = arg(0);
|
||||
if (!bound || !shift) return std::nullopt;
|
||||
return fits(*bound << (*shift & 31u));
|
||||
}
|
||||
case IrOpcode::ConvertU32U16:
|
||||
return 0xffffu;
|
||||
case IrOpcode::Select:
|
||||
case IrOpcode::SelectU32:
|
||||
{
|
||||
if (value.Argument(0)->Resolve()->Opcode() == IrOpcode::GetExec || value.Argument(2)->Resolve()->Opcode() == IrOpcode::LoadVectorWindow) return arg(1);
|
||||
const auto lhs = arg(1);
|
||||
const auto rhs = arg(2);
|
||||
if (lhs && rhs) return std::max(*lhs, *rhs);
|
||||
return lhs ? lhs : rhs;
|
||||
}
|
||||
case IrOpcode::ReadFirstLane:
|
||||
return arg(0);
|
||||
case IrOpcode::Identity:
|
||||
case IrOpcode::Phi: {
|
||||
Bound bound;
|
||||
for (std::size_t index = 0; index < value.ArgumentCount(); ++index) {
|
||||
if (const auto incoming = arg(index)) bound = std::max(bound.value_or(0u), *incoming);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
case IrOpcode::LoadVectorWindow: {
|
||||
const IrValue* index = value.Argument(0)->Resolve();
|
||||
if (!index->HasImmediate()) return std::nullopt;
|
||||
Bound bound = 0u;
|
||||
for (const IrValue* stored : reachingStores(value, index->ImmediateU32())) {
|
||||
bound = maxOf({bound, Of(*stored)});
|
||||
if (!bound) return std::nullopt;
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<const IrValue*, Bound> memo;
|
||||
std::unordered_set<const IrValue*> active;
|
||||
};
|
||||
|
||||
std::uint32_t IndexedWindowSlots(const IrProgram& program) {
|
||||
const auto window = program.Metadata().vectorWindowSize;
|
||||
WindowIndexBounds bounds;
|
||||
std::uint64_t reached = 0;
|
||||
for (const auto& block : program.Blocks()) {
|
||||
for (const IrValue* inst : block->Instructions()) {
|
||||
if (inst->Opcode() != IrOpcode::LoadVectorWindow && inst->Opcode() != IrOpcode::StoreVectorWindow) continue;
|
||||
const IrValue* index = inst->Argument(0)->Resolve();
|
||||
if (index->HasImmediate()) continue;
|
||||
const auto bound = bounds.Of(*index);
|
||||
if (bound && *bound < window / 2u) reached = std::max(reached, *bound + 1u);
|
||||
}
|
||||
}
|
||||
return static_cast<std::uint32_t>(reached);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DefineModule(SpirvEmitterState& state) {
|
||||
state.interfaceVariables.reserve(state.program.Info().inputs.size() + state.program.Info().outputs.size());
|
||||
DefineInputs(state);
|
||||
@@ -55,9 +224,14 @@ void DefineModule(SpirvEmitterState& state) {
|
||||
state.scratchVariable.at(half) = state.module.AllocateId();
|
||||
}
|
||||
}
|
||||
if (state.program.Metadata().vectorWindowSize != 0u) {
|
||||
if (const auto window = state.program.Metadata().vectorWindowSize; window != 0u) {
|
||||
static const bool narrowWindow = std::getenv("APS5_NO_WINDOW_NARROWING") == nullptr;
|
||||
state.vectorWindowIndexed = narrowWindow ? IndexedWindowSlots(state.program) : 0u;
|
||||
for (std::uint32_t half = 0; half < state.laneCount; half++) {
|
||||
state.vectorWindowVariable.at(half) = state.module.AllocateId();
|
||||
if (state.vectorWindowIndexed == 0u) continue;
|
||||
auto& slots = state.vectorWindowSlots.at(half);
|
||||
for (std::uint32_t slot = state.vectorWindowIndexed; slot < window; slot++) slots.push_back(state.module.AllocateId());
|
||||
}
|
||||
}
|
||||
state.mainFunc = state.module.AllocateId();
|
||||
|
||||
@@ -1053,10 +1053,14 @@ void EmitProgram(SpirvEmitterState& state) {
|
||||
}
|
||||
}
|
||||
if (const auto window = state.program.Metadata().vectorWindowSize; window != 0u) {
|
||||
const auto array = state.module.Type(spv::OpTypeArray, TypeU32(state), ConstantU32(state, window));
|
||||
const auto indexed = state.vectorWindowIndexed != 0u ? state.vectorWindowIndexed : window;
|
||||
const auto array = state.module.Type(spv::OpTypeArray, TypeU32(state), ConstantU32(state, indexed));
|
||||
const auto zero = state.module.Constant(spv::OpConstantNull, array);
|
||||
for (std::uint32_t half = 0; half < state.laneCount; half++) {
|
||||
state.module.AddFunction(spv::OpVariable, TypePointer(state, spv::StorageClassFunction, array), state.vectorWindowVariable.at(half), spv::StorageClassFunction, zero);
|
||||
for (const auto slot : state.vectorWindowSlots.at(half)) {
|
||||
state.module.AddFunction(spv::OpVariable, TypePointer(state, spv::StorageClassFunction, TypeU32(state)), slot, spv::StorageClassFunction, ConstantU32(state, 0u));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.pixelValidMaskVariable != 0u) {
|
||||
|
||||
Reference in new issue
Block a user