perf(shader): build a shader's front end once on first use
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d3ec1f15fb
commit
81961ca91b
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef CORE_SHADER_RECOMPILIER_INTERMEDIATEREPRESENTATION_INCLUDE_INTERMEDIATEREPRESENTATION_IRMETADATA_RESOURCEPLAN_HPP
|
||||
#define CORE_SHADER_RECOMPILIER_INTERMEDIATEREPRESENTATION_INCLUDE_INTERMEDIATEREPRESENTATION_IRMETADATA_RESOURCEPLAN_HPP
|
||||
|
||||
#include "IntermediateRepresentation/IrBlock.hpp"
|
||||
#include "IntermediateRepresentation/IrValue.hpp"
|
||||
#include "IntermediateRepresentation/IrMetadata/ControlFlowInfo.hpp"
|
||||
#include "IntermediateRepresentation/IrMetadata/DescriptorBinding.hpp"
|
||||
|
|
@ -55,6 +56,7 @@ struct IrResourcePlan {
|
|||
std::uint32_t userDataBase = 0;
|
||||
std::uint32_t userDataCount = 64;
|
||||
std::vector<std::unique_ptr<IrValue>> valueStorage;
|
||||
std::vector<std::unique_ptr<IrBlock>> blockStorage;
|
||||
std::vector<MemoryInfo> memoryInfo;
|
||||
std::vector<DescriptorSource> descriptorSources;
|
||||
std::vector<ResourceBlock> controlFlow;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
|
@ -794,6 +795,68 @@ void ResourceMaterializer::Apply(IrProgram& program, const ResourceSpecializatio
|
|||
resources.memoryInfo = std::move(memoryInfo);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void ownPlanValues(IrResourcePlan& plan) {
|
||||
std::vector<IrValue**> roots;
|
||||
for (auto& source : plan.descriptorSources) {
|
||||
for (auto& dword : source.dwords) roots.push_back(&dword);
|
||||
}
|
||||
for (auto& read : plan.srtReads) roots.push_back(&read.value);
|
||||
for (auto& block : plan.controlFlow) roots.push_back(&block.condition);
|
||||
for (auto& value : plan.uniformFill.values) roots.push_back(&value);
|
||||
|
||||
std::unordered_map<const IrValue*, IrValue*> clones;
|
||||
std::vector<const IrValue*> order;
|
||||
std::vector<const IrValue*> pending;
|
||||
for (const auto* root : roots) {
|
||||
if (*root != nullptr) pending.push_back(*root);
|
||||
}
|
||||
while (!pending.empty()) {
|
||||
const auto* value = pending.back();
|
||||
pending.pop_back();
|
||||
if (!clones.emplace(value, nullptr).second) continue;
|
||||
order.push_back(value);
|
||||
for (const auto* argument : value->Arguments()) {
|
||||
if (argument != nullptr) pending.push_back(argument);
|
||||
}
|
||||
}
|
||||
for (const auto* value : order) {
|
||||
auto clone = std::make_unique<IrValue>(value->Opcode(), value->Type(), value->Id());
|
||||
clone->SetFlags(value->Flags<std::uint64_t>());
|
||||
if (value->HasImmediate()) clone->SetImmediateU64(value->ImmediateU64());
|
||||
clone->SetRegister(value->Register());
|
||||
clones[value] = clone.get();
|
||||
plan.valueStorage.push_back(std::move(clone));
|
||||
}
|
||||
std::unordered_map<const IrBlock*, IrBlock*> blocks;
|
||||
const auto blockFor = [&](const IrBlock* block) {
|
||||
auto& clone = blocks[block];
|
||||
if (clone == nullptr) {
|
||||
plan.blockStorage.push_back(std::make_unique<IrBlock>(block->Id()));
|
||||
clone = plan.blockStorage.back().get();
|
||||
}
|
||||
return clone;
|
||||
};
|
||||
for (const auto* value : order) {
|
||||
auto* clone = clones.at(value);
|
||||
for (std::size_t index = 0; index < value->ArgumentCount(); index++) {
|
||||
const auto* argument = value->Argument(index);
|
||||
auto* mapped = argument == nullptr ? nullptr : clones.at(argument);
|
||||
if (value->IsPhi()) {
|
||||
clone->AddPhiOperand(blockFor(value->PhiBlock(index)), mapped);
|
||||
} else {
|
||||
clone->AddArgument(mapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto* root : roots) {
|
||||
if (*root != nullptr) *root = clones.at(*root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IrResourcePlan ResourceMaterializer::ExtractPlan(const IrProgram& program) const {
|
||||
const IrResourcePlan& source = program.Resources();
|
||||
if (!source.resourceTrackingComplete || !source.srtPlanComplete) {
|
||||
|
|
@ -814,6 +877,7 @@ IrResourcePlan ResourceMaterializer::ExtractPlan(const IrProgram& program) const
|
|||
plan.resourceTrackingComplete = source.resourceTrackingComplete;
|
||||
plan.info = source.info;
|
||||
plan.uniformFill = source.uniformFill;
|
||||
ownPlanValues(plan);
|
||||
const auto addSource = [&plan](std::uint32_t index) {
|
||||
if (index >= plan.descriptorSources.size()) {
|
||||
throw std::runtime_error("ResourceMaterializer::ExtractPlan resource references an unknown descriptor source");
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ struct SourceEntry {
|
|||
// rethrown: the front end ran every pass before failing, ~13 ms per dispatch of a shader the
|
||||
// title issues every frame (0x1048947300 at the intro video). APS5_NO_FAILURE_MEMO=1 rebuilds.
|
||||
std::exception_ptr planFailure;
|
||||
std::unique_ptr<IrProgram> program;
|
||||
std::vector<std::shared_ptr<const CompiledVariant>> variants;
|
||||
// The result memo, most recently used first, at most ResultMemoEntries (under mutex).
|
||||
std::list<ResultMemoEntry> memo;
|
||||
|
|
@ -197,15 +198,14 @@ struct SourceEntry {
|
|||
namespace {
|
||||
|
||||
struct ResourceProgram {
|
||||
explicit ResourceProgram(const RecompileRequest& request) : program(PrepareResourceProgram(request)), plan(ResourceMaterializer{}.ExtractPlan(program)) {}
|
||||
explicit ResourceProgram(const RecompileRequest& request) : program(std::make_unique<IrProgram>(PrepareResourceProgram(request))), plan(std::make_shared<const IrResourcePlan>(ResourceMaterializer{}.ExtractPlan(*program))) {}
|
||||
|
||||
IrProgram program;
|
||||
IrResourcePlan plan;
|
||||
std::unique_ptr<IrProgram> program;
|
||||
std::shared_ptr<const IrResourcePlan> plan;
|
||||
};
|
||||
|
||||
std::shared_ptr<const IrResourcePlan> makeResourcePlan(const RecompileRequest& request) {
|
||||
const auto resource = std::make_shared<ResourceProgram>(request);
|
||||
return std::shared_ptr<const IrResourcePlan>(resource, &resource->plan);
|
||||
return ResourceProgram(request).plan;
|
||||
}
|
||||
|
||||
struct SourceKeyHash {
|
||||
|
|
@ -263,7 +263,9 @@ std::shared_ptr<SourceEntry> getSource(const RecompileRequest& request) {
|
|||
static const bool memoFailures = std::getenv("APS5_NO_FAILURE_MEMO") == nullptr;
|
||||
if (memoFailures && source->planFailure) std::rethrow_exception(source->planFailure);
|
||||
try {
|
||||
source->plan = makeResourcePlan(request);
|
||||
ResourceProgram resource(request);
|
||||
source->plan = std::move(resource.plan);
|
||||
source->program = std::move(resource.program);
|
||||
} catch (...) {
|
||||
if (memoFailures) source->planFailure = std::current_exception();
|
||||
throw;
|
||||
|
|
@ -389,10 +391,12 @@ std::shared_ptr<const CompiledVariant> findOrCompileVariant(SourceEntry& source,
|
|||
}
|
||||
}
|
||||
if (variant == nullptr) {
|
||||
auto program = PrepareResourceProgram(request);
|
||||
auto program = source.program != nullptr ? std::move(*source.program) : PrepareResourceProgram(request);
|
||||
source.program.reset();
|
||||
variant = std::make_shared<const CompiledVariant>(compileVariant(request, std::move(program), snapshot, specialization));
|
||||
if (disk) ShaderDiskCache::Store(std::move(diskKey), variant);
|
||||
}
|
||||
source.program.reset();
|
||||
source.variants.push_back(variant);
|
||||
return variant;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue