feat(shader): implement enable cache field in recompile request
This commit is contained in:
parent
e98d2163f4
commit
bf43995e8e
|
|
@ -355,7 +355,7 @@ private:
|
|||
request.context.memory = captured;
|
||||
timing.Mark("request_memory");
|
||||
const auto compiled = ShaderRecompiler::Recompile(request);
|
||||
timing.Mark("shader_compile");
|
||||
timing.Mark(compiled.cacheHit ? "shader_cache_hit" : "shader_compile");
|
||||
std::vector<Graphics::GuestMemorySnapshot> snapshots;
|
||||
for (const auto& region : captured) snapshots.push_back({region.guestAddress, region.bytes});
|
||||
timing.Mark("snapshots");
|
||||
|
|
@ -481,7 +481,7 @@ private:
|
|||
request.context.memory = memory;
|
||||
shaderTiming.Mark("request_memory");
|
||||
results.push_back(ShaderRecompiler::Recompile(request));
|
||||
shaderTiming.Mark("compile");
|
||||
shaderTiming.Mark(results.back().cacheHit ? "cache_hit" : "compile");
|
||||
const auto& result = results.back();
|
||||
if (!drawParameters.indexed && i == 0) {
|
||||
const auto offsetValue = [&](std::int32_t sgpr) {
|
||||
|
|
|
|||
|
|
@ -46,9 +46,8 @@ bool ShaderMemory::read(void* context, std::uint64_t address, std::uint32_t* val
|
|||
}
|
||||
|
||||
void ShaderMemory::Capture(const ShaderRecompiler::RecompileRequest& request) {
|
||||
auto program = ShaderRecompiler::PrepareResourceProgram(request);
|
||||
const auto plan = ShaderRecompiler::GetResourcePlan(request);
|
||||
constexpr ShaderRecompiler::ResourceMaterializer materializer;
|
||||
const auto plan = materializer.ExtractPlan(program);
|
||||
ShaderRecompiler::SrtRuntime runtime;
|
||||
runtime.userData = request.context.userData;
|
||||
runtime.shaderBase = request.shader.codeAddress;
|
||||
|
|
@ -57,7 +56,7 @@ void ShaderMemory::Capture(const ShaderRecompiler::RecompileRequest& request) {
|
|||
runtime.readSpecializationMemory = &read;
|
||||
ShaderRecompiler::ResourceSnapshot snapshot;
|
||||
ShaderRecompiler::ResourceSpecialization specialization;
|
||||
materializer.Materialize(plan, runtime, snapshot, specialization);
|
||||
materializer.Materialize(*plan, runtime, snapshot, specialization);
|
||||
}
|
||||
|
||||
std::vector<ShaderRecompiler::MemoryRegion> ShaderMemory::Regions() const {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#endif
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -115,6 +116,38 @@ int main() {
|
|||
request.context.memory = regions;
|
||||
const auto first = Recompile(request);
|
||||
require(!first.spirv.empty(), "empty compiled shader");
|
||||
require(!first.cacheHit, "first shader compilation unexpectedly hit the cache");
|
||||
const auto plan = GetResourcePlan(request);
|
||||
require(plan == GetResourcePlan(request), "resource plan was rebuilt");
|
||||
const auto cached = Recompile(request);
|
||||
require(cached.cacheHit, "unchanged shader did not hit the cache");
|
||||
verifyResult(first, cached);
|
||||
auto relocated = request;
|
||||
relocated.shader.codeAddress += 0x1000;
|
||||
require(Recompile(relocated).cacheHit, "shader relocation caused recompilation");
|
||||
auto changedTarget = request;
|
||||
changedTarget.target.subgroupSize = 32;
|
||||
require(GetResourcePlan(changedTarget) != plan, "different target reused the source entry");
|
||||
std::vector<std::uint32_t> changedCode(code.begin(), code.end());
|
||||
changedCode.insert(changedCode.begin(), 0xbf800000u);
|
||||
auto changedSource = request;
|
||||
changedSource.shader.code = changedCode;
|
||||
require(GetResourcePlan(changedSource) != plan, "changed code reused the source entry");
|
||||
auto uncached = request;
|
||||
uncached.useCache = false;
|
||||
require(GetResourcePlan(uncached) != plan, "disabled cache reused the resource plan");
|
||||
const auto fresh = Recompile(uncached);
|
||||
require(!fresh.cacheHit, "disabled cache reused the compiled variant");
|
||||
verifyResult(first, fresh);
|
||||
require(!RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(uncached)).request.useCache, "cache policy was lost in serialization");
|
||||
auto changedLayout = request;
|
||||
changedLayout.layout.pushConstantSizeBytes = 64;
|
||||
require(!Recompile(changedLayout).cacheHit, "binding layout change reused an incompatible variant");
|
||||
require(Recompile(changedLayout).cacheHit, "new binding layout variant was not cached");
|
||||
require(Recompile(request).cacheHit, "compiling a new variant evicted the original");
|
||||
auto missingMemory = request;
|
||||
missingMemory.context.memory = {};
|
||||
expectFailure([&] { static_cast<void>(Recompile(missingMemory)); }, "SrtWalker::EvaluateRuntimeSources", "cache hit bypassed resource validation");
|
||||
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
||||
auto invalidSpirv = first.spirv;
|
||||
invalidSpirv[0] = 0;
|
||||
|
|
@ -122,6 +155,29 @@ int main() {
|
|||
expectFailure([&] { static_cast<void>(ValidateAndOptimizeSpirv(first.spirv, 0x00400000u, 0x00010600u)); }, "unsupported Vulkan/SPIR-V target", "incompatible target accepted");
|
||||
expectFailure([&] { static_cast<void>(ValidateAndOptimizeSpirv(first.spirv, 0x00405000u, 0x00010600u)); }, "unsupported Vulkan target", "unknown Vulkan target accepted");
|
||||
#endif
|
||||
payload = 0x40000000u;
|
||||
AgcDriver::ShaderMemory updatedMemory({});
|
||||
updatedMemory.Capture(request);
|
||||
const auto updatedRegions = updatedMemory.Regions();
|
||||
auto updated = request;
|
||||
updated.context.memory = updatedRegions;
|
||||
const auto updatedCached = Recompile(updated);
|
||||
require(updatedCached.cacheHit, "dynamic shader data caused recompilation");
|
||||
updated.useCache = false;
|
||||
verifyResult(updatedCached, Recompile(updated));
|
||||
bool changedData = updatedCached.pushConstants != first.pushConstants;
|
||||
for (std::size_t i = 0; i < first.bindings.size(); ++i) changedData = changedData || updatedCached.bindings.at(i).guestDescriptor != first.bindings[i].guestDescriptor;
|
||||
require(changedData, "cache hit retained stale shader data");
|
||||
auto concurrent = request;
|
||||
concurrent.layout.pushConstantSizeBytes = 60;
|
||||
std::array<std::future<RecompileResult>, 4> concurrentResults;
|
||||
for (auto& future : concurrentResults) future = std::async(std::launch::async, [concurrent] { return Recompile(concurrent); });
|
||||
std::uint32_t compilations = 0;
|
||||
for (auto& future : concurrentResults) {
|
||||
const auto result = future.get();
|
||||
if (!result.cacheHit) ++compilations;
|
||||
}
|
||||
require(compilations == 1, "concurrent requests compiled the same variant repeatedly");
|
||||
const auto serialized = RequestSerializer{}.Serialize(request);
|
||||
table = 0;
|
||||
payload = 0xdeadbeefu;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
#ifndef CORE_SHADER_RECOMPILER_CACHEKEY_HPP
|
||||
#define CORE_SHADER_RECOMPILER_CACHEKEY_HPP
|
||||
|
||||
#include "Recompiler.hpp"
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
class RecompileCacheKey {
|
||||
public:
|
||||
static void Build(const RecompileRequest& request, std::vector<std::uint64_t>& key) {
|
||||
key.clear();
|
||||
append(key, request.shader.stage);
|
||||
append(key, request.shader.code);
|
||||
append(key, request.context.waveSize);
|
||||
append(key, request.context.userDataBaseRegister);
|
||||
append(key, request.context.userData.size());
|
||||
append(key, request.context.compute);
|
||||
append(key, request.context.pixel);
|
||||
append(key, request.context.vertex);
|
||||
append(key, request.target);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename TValue>
|
||||
static void append(std::vector<std::uint64_t>& key, TValue value) requires (std::is_integral_v<TValue> || std::is_enum_v<TValue>) {
|
||||
key.push_back(static_cast<std::uint64_t>(value));
|
||||
}
|
||||
|
||||
template<typename TValue, std::size_t TSize>
|
||||
static void append(std::vector<std::uint64_t>& key, const std::array<TValue, TSize>& values) {
|
||||
for (const auto value : values) append(key, value);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
static void append(std::vector<std::uint64_t>& key, std::span<TValue> values) {
|
||||
append(key, values.size());
|
||||
for (const auto value : values) append(key, value);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
static void append(std::vector<std::uint64_t>& key, const std::optional<TValue>& value) {
|
||||
append(key, value.has_value());
|
||||
if (value) append(key, *value);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, std::string_view value) {
|
||||
append(key, value.size());
|
||||
for (const unsigned char byte : value) append(key, byte);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderComputeStageInfo& value) {
|
||||
append(key, value.numThreads);
|
||||
append(key, value.ldsSizeDwords);
|
||||
append(key, value.groupIdEnable);
|
||||
append(key, value.tgSizeEnable);
|
||||
append(key, value.threadIdComponentCount);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderPixelStageInfo& value) {
|
||||
append(key, value.interpolatorCount);
|
||||
if (value.interpolatorCount > value.interpolatorSettings.size()) throw std::runtime_error("Shader cache: invalid interpolator count");
|
||||
for (std::uint32_t i = 0; i < value.interpolatorCount; ++i) append(key, value.interpolatorSettings[i]);
|
||||
append(key, value.wave32);
|
||||
append(key, value.perspectiveCenterVgpr);
|
||||
append(key, value.hasPerspectiveCenterVgpr);
|
||||
append(key, value.posX);
|
||||
append(key, value.posY);
|
||||
append(key, value.posZ);
|
||||
append(key, value.posW);
|
||||
append(key, value.frontFace);
|
||||
append(key, value.ancillary);
|
||||
append(key, value.sampleShading);
|
||||
append(key, value.noPerspective);
|
||||
append(key, value.pixelKillEnable);
|
||||
append(key, value.depthExportEnable);
|
||||
append(key, value.sampleMaskExportEnable);
|
||||
append(key, value.earlyZ);
|
||||
append(key, value.executeOnNoop);
|
||||
append(key, value.targetOutputMode);
|
||||
append(key, value.targetExportMapping);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderVertexResourceDestination& value) {
|
||||
append(key, value.registerStart);
|
||||
append(key, value.registersNum);
|
||||
append(key, value.attrId);
|
||||
append(key, value.fetchIndex);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderVertexStageInfo& value) {
|
||||
append(key, value.resourcesNum);
|
||||
append(key, value.fetchAttribReg);
|
||||
append(key, value.fetchBufferReg);
|
||||
append(key, value.fetchEmbedded);
|
||||
if (value.resourcesNum > value.resources.size()) throw std::runtime_error("Shader cache: invalid vertex resource count");
|
||||
for (std::uint32_t i = 0; i < value.resourcesNum; ++i) {
|
||||
append(key, value.resources[i].fields[1] & 0xffff0000u);
|
||||
append(key, value.resources[i].fields[3]);
|
||||
append(key, value.resourcesDst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const MeshTargetLimits& value) {
|
||||
append(key, value.maxWorkgroupSize);
|
||||
append(key, value.maxWorkgroupInvocations);
|
||||
append(key, value.maxSharedMemoryBytes);
|
||||
append(key, value.maxOutputVertices);
|
||||
append(key, value.maxOutputPrimitives);
|
||||
append(key, value.maxOutputComponents);
|
||||
append(key, value.maxOutputMemoryBytes);
|
||||
append(key, value.outputPerVertexGranularity);
|
||||
append(key, value.outputPerPrimitiveGranularity);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const TessellationTargetLimits& value) {
|
||||
append(key, value.maxPatchSize);
|
||||
append(key, value.maxControlPerVertexInputComponents);
|
||||
append(key, value.maxControlPerVertexOutputComponents);
|
||||
append(key, value.maxControlPerPatchOutputComponents);
|
||||
append(key, value.maxControlTotalOutputComponents);
|
||||
append(key, value.maxEvaluationInputComponents);
|
||||
append(key, value.maxEvaluationOutputComponents);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const SpirvTarget& value) {
|
||||
append(key, value.vulkanVersion);
|
||||
append(key, value.spirvVersion);
|
||||
append(key, value.subgroupSize);
|
||||
append(key, value.bdaAbiVersion);
|
||||
append(key, value.supportedCapabilities);
|
||||
append(key, value.supportedExtensions);
|
||||
append(key, value.fragmentShaderBarycentricEnabled);
|
||||
append(key, value.maxWorkgroupSize);
|
||||
append(key, value.maxWorkgroupInvocations);
|
||||
append(key, value.maxWorkgroupSharedMemoryBytes);
|
||||
append(key, value.mesh);
|
||||
append(key, value.tessellation);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -660,7 +660,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(1u);
|
||||
writer.WriteU32(2u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
|
|
@ -669,13 +669,16 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
if (request.graphics.has_value()) {
|
||||
writeGraphicsCompileContext(writer, *request.graphics);
|
||||
}
|
||||
writer.WriteBool(request.useCache);
|
||||
return base64Encode(buffer);
|
||||
}
|
||||
|
||||
DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const {
|
||||
const std::string decoded = base64Decode(text);
|
||||
Reader reader(decoded);
|
||||
if (reader.ReadU32() != 0x41505335u || reader.ReadU32() != 1u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (reader.ReadU32() != 0x41505335u) throw std::runtime_error("invalid recompile request signature");
|
||||
const auto version = reader.ReadU32();
|
||||
if (version != 1u && version != 2u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
DeserializedRequest result{};
|
||||
result.request.shader = readShaderBinary(reader, result.shaderCode, result.shaderHeader);
|
||||
result.request.context = readGuestContext(reader, result);
|
||||
|
|
@ -685,6 +688,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const
|
|||
result.graphicsStorage = std::make_unique<DeserializedGraphicsCompileContext>();
|
||||
result.request.graphics = readGraphicsCompileContext(reader, *result.graphicsStorage);
|
||||
}
|
||||
if (version == 2u) result.request.useCache = reader.ReadBool();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace ShaderRecompiler {
|
|||
class DescriptorBindingBuilder {
|
||||
public:
|
||||
void Populate(BindingAllocationResult& allocation, const IrProgram& program, const ResourceSnapshot& snapshot) const;
|
||||
void Populate(BindingAllocationResult& allocation, const ShaderInfo& info, IrShaderStage stage, std::uint32_t userDataBase, const ResourceSnapshot& snapshot) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
namespace ShaderRecompiler {
|
||||
|
||||
[[nodiscard]] IrProgram PrepareResourceProgram(const RecompileRequest& request);
|
||||
[[nodiscard]] std::shared_ptr<const IrResourcePlan> GetResourcePlan(const RecompileRequest& request);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -163,14 +163,14 @@ std::vector<std::uint32_t> GuestSamplersDescriptor(const std::vector<std::uint32
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> ShaderDataDwordsFor(const IrBindingLayout& layout, const IrResourcePlan& resources, const ResourceSnapshot& snapshot) {
|
||||
std::vector<std::uint32_t> ShaderDataDwordsFor(const IrBindingLayout& layout, std::uint32_t userDataBase, const ResourceSnapshot& snapshot) {
|
||||
std::vector<std::uint32_t> result(layout.ShaderDataDwords(), 0u);
|
||||
for (std::size_t i = 0; i < layout.userDataRegisters.size(); i++) {
|
||||
const std::uint32_t reg = layout.userDataRegisters[i];
|
||||
if (reg < resources.userDataBase || reg - resources.userDataBase >= snapshot.userData.size()) {
|
||||
if (reg < userDataBase || reg - userDataBase >= snapshot.userData.size()) {
|
||||
fail("DescriptorBindingBuilder::Populate user-data register is out of range");
|
||||
}
|
||||
result[i] = snapshot.userData[reg - resources.userDataBase];
|
||||
result[i] = snapshot.userData[reg - userDataBase];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -178,9 +178,12 @@ std::vector<std::uint32_t> ShaderDataDwordsFor(const IrBindingLayout& layout, co
|
|||
}
|
||||
|
||||
void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, const IrProgram& program, const ResourceSnapshot& snapshot) const {
|
||||
Populate(allocation, program.Info(), program.Resources().stage, program.Resources().userDataBase, snapshot);
|
||||
}
|
||||
|
||||
void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, const ShaderInfo& info, IrShaderStage stage, std::uint32_t userDataBase, const ResourceSnapshot& snapshot) const {
|
||||
const IrBindingLayout& layout = allocation.layout;
|
||||
const IrShaderStage stage = program.Resources().stage;
|
||||
const std::vector<std::uint32_t> shaderData = ShaderDataDwordsFor(layout, program.Resources(), snapshot);
|
||||
const std::vector<std::uint32_t> shaderData = ShaderDataDwordsFor(layout, userDataBase, snapshot);
|
||||
|
||||
std::vector<DescriptorBinding> bindings;
|
||||
bindings.reserve(layout.descriptors.size());
|
||||
|
|
@ -199,12 +202,12 @@ void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, con
|
|||
break;
|
||||
case DescriptorRole::GuestImages:
|
||||
physical.guestDescriptor = GuestImagesDescriptor(logical.resources, snapshot);
|
||||
physical.imageShape = ImageShapeFor(program.Info().images, logical.resources);
|
||||
physical.imageShape = ImageShapeFor(info.images, logical.resources);
|
||||
break;
|
||||
case DescriptorRole::GuestSamplers:
|
||||
physical.guestDescriptor = GuestSamplersDescriptor(logical.resources, snapshot);
|
||||
for (std::size_t element = 0; element < logical.resources.size(); ++element) {
|
||||
const auto& sampler = program.Info().samplers.at(logical.resources[element]);
|
||||
const auto& sampler = info.samplers.at(logical.resources[element]);
|
||||
physical.samplerDepthCompare.push_back(sampler.depthCompare);
|
||||
if (sampler.forcePointFiltering) {
|
||||
auto& filter = physical.guestDescriptor.at(element * 4u + 2u);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
#include "Recompiler.hpp"
|
||||
#include "CacheKey.hpp"
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include "ControlFlow/include/ControlFlow/GraphBuilder.hpp"
|
||||
#include "ControlFlow/include/ControlFlow/Structurizer.hpp"
|
||||
#include "RdnaDecoder/include/RdnaDecoder/RdnaInstructionDecoder.hpp"
|
||||
|
|
@ -121,21 +125,76 @@ IrProgram PrepareResourceProgram(const RecompileRequest& request) {
|
|||
|
||||
namespace {
|
||||
|
||||
RecompileResult RecompileImpl(const RecompileRequest& request) {
|
||||
struct CompiledVariant {
|
||||
ResourceSpecialization specialization;
|
||||
BindingLayout layout;
|
||||
CompiledShaderInfo info;
|
||||
BindingAllocationResult bindings;
|
||||
RecompileResult result;
|
||||
};
|
||||
|
||||
auto program = PrepareResourceProgram(request);
|
||||
struct ResourceProgram {
|
||||
explicit ResourceProgram(const RecompileRequest& request) : program(PrepareResourceProgram(request)), plan(ResourceMaterializer{}.ExtractPlan(program)) {}
|
||||
|
||||
IrProgram program;
|
||||
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);
|
||||
}
|
||||
|
||||
struct SourceEntry {
|
||||
std::mutex mutex;
|
||||
std::shared_ptr<const IrResourcePlan> plan;
|
||||
std::vector<std::shared_ptr<const CompiledVariant>> variants;
|
||||
};
|
||||
|
||||
struct SourceKeyHash {
|
||||
std::size_t operator()(const std::vector<std::uint64_t>& key) const {
|
||||
std::size_t hash = 0;
|
||||
for (const auto value : key) {
|
||||
hash ^= static_cast<std::size_t>(value) + static_cast<std::size_t>(0x9e3779b97f4a7c15ull) + (hash << 6u) + (hash >> 2u);
|
||||
if constexpr (sizeof(std::size_t) < sizeof(value)) hash ^= static_cast<std::size_t>(value >> 32u);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<SourceEntry> getSource(const RecompileRequest& request) {
|
||||
static std::shared_mutex mutex;
|
||||
static std::unordered_map<std::vector<std::uint64_t>, std::shared_ptr<SourceEntry>, SourceKeyHash> sources;
|
||||
thread_local std::vector<std::uint64_t> key;
|
||||
RecompileCacheKey::Build(request, key);
|
||||
std::shared_ptr<SourceEntry> source;
|
||||
{
|
||||
std::shared_lock lock(mutex);
|
||||
const auto found = sources.find(key);
|
||||
if (found != sources.end()) source = found->second;
|
||||
}
|
||||
if (source == nullptr) {
|
||||
std::unique_lock lock(mutex);
|
||||
const auto found = sources.find(key);
|
||||
if (found != sources.end()) source = found->second;
|
||||
else {
|
||||
source = std::make_shared<SourceEntry>();
|
||||
sources.emplace(key, source);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(source->mutex);
|
||||
if (source->plan == nullptr) {
|
||||
source->plan = makeResourcePlan(request);
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, const ResourceSnapshot& resourceSnapshot, const ResourceSpecialization& resourceSpecialization) {
|
||||
const auto inputInfo = BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context);
|
||||
constexpr DeadCodeEliminator deadCodeEliminator;
|
||||
|
||||
constexpr ResourceMaterializer resourceMaterializer;
|
||||
const auto resourcePlan = resourceMaterializer.ExtractPlan(program);
|
||||
|
||||
RequestMemoryView requestMemoryView(request.context.memory);
|
||||
const auto srtRuntime = requestMemoryView.MakeRuntime(request.context.userData, request.shader.codeAddress);
|
||||
|
||||
ResourceSnapshot resourceSnapshot;
|
||||
ResourceSpecialization resourceSpecialization;
|
||||
resourceMaterializer.Materialize(resourcePlan, srtRuntime, resourceSnapshot, resourceSpecialization);
|
||||
resourceMaterializer.Apply(program, resourceSpecialization);
|
||||
|
||||
deadCodeEliminator.RemoveIdentities(program);
|
||||
|
|
@ -167,8 +226,6 @@ RecompileResult RecompileImpl(const RecompileRequest& request) {
|
|||
#endif
|
||||
|
||||
result.bdaAbiVersion = program.Info().usesDma ? request.target.bdaAbiVersion : 0u;
|
||||
result.bindings = bindings.bindings;
|
||||
result.pushConstants = bindings.pushConstants;
|
||||
result.vertexOffsetSgpr = program.Info().vertexOffsetSgpr;
|
||||
result.instanceOffsetSgpr = program.Info().instanceOffsetSgpr;
|
||||
for (const auto& output : program.Info().outputs) {
|
||||
|
|
@ -184,9 +241,78 @@ RecompileResult RecompileImpl(const RecompileRequest& request) {
|
|||
}
|
||||
}
|
||||
|
||||
result.bindings.clear();
|
||||
result.pushConstants.clear();
|
||||
for (auto& attribute : result.vertexAttributes) attribute.resource = {};
|
||||
bindings.bindings.clear();
|
||||
bindings.pushConstants.clear();
|
||||
return {resourceSpecialization, request.layout, std::move(program).TakeCompiledInfo(), std::move(bindings), std::move(result)};
|
||||
}
|
||||
|
||||
RecompileResult materializeResult(const CompiledVariant& variant, const RecompileRequest& request, const ResourceSnapshot& snapshot) {
|
||||
auto result = variant.result;
|
||||
BindingAllocationResult bindings;
|
||||
bindings.layout = variant.bindings.layout;
|
||||
bindings.pushConstantOffsetBytes = variant.bindings.pushConstantOffsetBytes;
|
||||
bindings.pushConstantSizeBytes = variant.bindings.pushConstantSizeBytes;
|
||||
DescriptorBindingBuilder{}.Populate(bindings, variant.info.info, variant.info.stage, variant.info.userDataBase, snapshot);
|
||||
result.bindings = std::move(bindings.bindings);
|
||||
result.pushConstants = std::move(bindings.pushConstants);
|
||||
for (auto& attribute : result.vertexAttributes) {
|
||||
if (!request.context.vertex || attribute.location >= request.context.vertex->resourcesNum) throw std::runtime_error("Shader cache: invalid vertex attribute metadata");
|
||||
attribute.resource = request.context.vertex->resources[attribute.location];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sameLayout(const BindingLayout& left, const BindingLayout& right) {
|
||||
return left.descriptorSet == right.descriptorSet && left.firstBinding == right.firstBinding && left.pushConstantOffsetBytes == right.pushConstantOffsetBytes && left.pushConstantSizeBytes == right.pushConstantSizeBytes;
|
||||
}
|
||||
|
||||
RecompileResult RecompileImpl(const RecompileRequest& request) {
|
||||
static_cast<void>(BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context));
|
||||
RequestMemoryView memory(request.context.memory);
|
||||
const auto runtime = memory.MakeRuntime(request.context.userData, request.shader.codeAddress);
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
constexpr ResourceMaterializer materializer;
|
||||
if (!request.useCache) {
|
||||
auto program = PrepareResourceProgram(request);
|
||||
const auto plan = materializer.ExtractPlan(program);
|
||||
materializer.Materialize(plan, runtime, snapshot, specialization);
|
||||
const auto variant = compileVariant(request, std::move(program), snapshot, specialization);
|
||||
return materializeResult(variant, request, snapshot);
|
||||
}
|
||||
const auto source = getSource(request);
|
||||
materializer.Materialize(*source->plan, runtime, snapshot, specialization);
|
||||
std::shared_ptr<const CompiledVariant> variant;
|
||||
bool cacheHit = false;
|
||||
{
|
||||
std::lock_guard lock(source->mutex);
|
||||
for (const auto& candidate : source->variants) {
|
||||
if (sameLayout(candidate->layout, request.layout) && candidate->specialization == specialization) {
|
||||
variant = candidate;
|
||||
cacheHit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (variant == nullptr) {
|
||||
auto program = PrepareResourceProgram(request);
|
||||
variant = std::make_shared<CompiledVariant>(compileVariant(request, std::move(program), snapshot, specialization));
|
||||
source->variants.push_back(variant);
|
||||
}
|
||||
}
|
||||
auto result = materializeResult(*variant, request, snapshot);
|
||||
result.cacheHit = cacheHit;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<const IrResourcePlan> GetResourcePlan(const RecompileRequest& request) {
|
||||
static_cast<void>(BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context));
|
||||
if (request.useCache) return getSource(request)->plan;
|
||||
return makeResourcePlan(request);
|
||||
}
|
||||
|
||||
RecompileResult Recompile(const RecompileRequest& request) {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ struct RecompileRequest {
|
|||
SpirvTarget target;
|
||||
BindingLayout layout;
|
||||
std::optional<GraphicsCompileContext> graphics;
|
||||
bool useCache = true;
|
||||
};
|
||||
|
||||
enum class DescriptorKind {
|
||||
|
|
@ -265,6 +266,7 @@ struct RecompileResult {
|
|||
std::int32_t instanceOffsetSgpr = -1;
|
||||
std::vector<std::uint32_t> parameterExports;
|
||||
std::vector<FragmentParameter> fragmentParameters;
|
||||
bool cacheHit = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] RecompileResult Recompile(const RecompileRequest& request);
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
|
|||
throw std::runtime_error("ShaderInputInfoBuilder: GuestContext.vertex is not set");
|
||||
}
|
||||
const auto& vertex = *context.vertex;
|
||||
if (vertex.resourcesNum > vertex.resources.size()) throw std::runtime_error("ShaderInputInfoBuilder: invalid vertex resource count");
|
||||
vertexStorage = ShaderVertexInputInfo{};
|
||||
vertexStorage.logicalStage = _toIrShaderStage(stage);
|
||||
vertexStorage.fetchEmbedded = vertex.fetchEmbedded;
|
||||
|
|
|
|||
Loading…
Reference in New Issue