mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
refactor(shader): remove resource specialization from artifact key
This commit is contained in:
1 parent
ab0f9985fa
commit
b2a4813307
22 files changed
+177
-333
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "ShaderDiskCache.hpp"
|
||||
#include "CacheKey.hpp"
|
||||
#include "ShaderCacheDirectory.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -217,7 +218,6 @@ struct SampleRequest {
|
||||
std::array<std::string_view, 1> extensions{"SPV_KHR_storage_buffer_storage_class"};
|
||||
std::array<std::byte, 16> header{};
|
||||
RecompileRequest request{};
|
||||
ResourceSpecialization specialization;
|
||||
std::uint32_t hostSubgroupSize = 32;
|
||||
|
||||
SampleRequest() {
|
||||
@@ -236,8 +236,6 @@ struct SampleRequest {
|
||||
request.target.maxWorkgroupInvocations = 1024;
|
||||
request.target.maxWorkgroupSharedMemoryBytes = 49152;
|
||||
request.layout = {0, 0, 0, 128};
|
||||
specialization.images = {ResourceSpecialization::Image{}};
|
||||
specialization.boundDescriptors = {0, 0};
|
||||
}
|
||||
|
||||
std::vector<std::byte> Key() {
|
||||
@@ -245,7 +243,7 @@ struct SampleRequest {
|
||||
request.context.userData = userData;
|
||||
request.target.supportedCapabilities = capabilities;
|
||||
std::vector<std::byte> key;
|
||||
ShaderDiskCache::BuildKey(request, hostSubgroupSize, specialization, key);
|
||||
ShaderDiskCache::BuildKey(request, hostSubgroupSize, key);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
@@ -377,19 +375,6 @@ void verifyKeySensitivity() {
|
||||
changes("the first binding", [](SampleRequest& sample) { sample.request.layout.firstBinding = 1; });
|
||||
changes("the push constant offset", [](SampleRequest& sample) { sample.request.layout.pushConstantOffsetBytes = 16; });
|
||||
changes("the push constant size", [](SampleRequest& sample) { sample.request.layout.pushConstantSizeBytes = 64; });
|
||||
changes("an image class", [](SampleRequest& sample) { sample.specialization.images[0].numericClass = IrTextureNumericClass::Float; });
|
||||
changes("an image dimension", [](SampleRequest& sample) { sample.specialization.images[0].dimension = static_cast<RdnaImageDimension>(1); });
|
||||
changes("an image mip count", [](SampleRequest& sample) { sample.specialization.images[0].mipCount = 2; });
|
||||
changes("an image conversion", [](SampleRequest& sample) { sample.specialization.images[0].conversionFormat = static_cast<IrBufferFormat>(1); });
|
||||
changes("an image swizzle", [](SampleRequest& sample) { sample.specialization.images[0].shaderSwizzle = 0; });
|
||||
changes("an image indirect root", [](SampleRequest& sample) { sample.specialization.images[0].indirectRoot = 0; });
|
||||
changes("an image mapping offset", [](SampleRequest& sample) { sample.specialization.images[0].indirectMappingOffset = 4; });
|
||||
changes("an image search depth", [](SampleRequest& sample) { sample.specialization.images[0].indirectSearchIterations = 2; });
|
||||
changes("an image cube flag", [](SampleRequest& sample) { sample.specialization.images[0].cube = true; });
|
||||
changes("an image FMASK flag", [](SampleRequest& sample) { sample.specialization.images[0].fmask = true; });
|
||||
changes("an image sRGB decode", [](SampleRequest& sample) { sample.specialization.images[0].srgbDecode = true; });
|
||||
changes("the image count", [](SampleRequest& sample) { sample.specialization.images.emplace_back(); });
|
||||
changes("the bound descriptors", [](SampleRequest& sample) { sample.specialization.boundDescriptors.push_back(1); });
|
||||
|
||||
SampleRequest moved;
|
||||
moved.userData[0] ^= 0x10000u;
|
||||
@@ -397,6 +382,33 @@ void verifyKeySensitivity() {
|
||||
moved.request.shader.headerAddress += 0x1000000u;
|
||||
moved.header[3] = std::byte{1};
|
||||
require(moved.Key() == key, "the key depends on the user data values or the addresses");
|
||||
std::array<std::byte, 32> descriptors{};
|
||||
const std::array regions{MemoryRegion{0x100000u, descriptors}};
|
||||
moved.request.context.memory = regions;
|
||||
std::fill(moved.userData.begin(), moved.userData.end(), 0xffffffffu);
|
||||
require(moved.Key() == key, "runtime descriptors changed the artifact key");
|
||||
SampleRequest vertex;
|
||||
vertex.request.shader.stage = ShaderStage::Vertex;
|
||||
vertex.request.context.compute.reset();
|
||||
vertex.request.context.vertex.emplace();
|
||||
auto& input = *vertex.request.context.vertex;
|
||||
input.resourcesNum = 1u;
|
||||
input.resources[0].fields = {0x10000u, 16u << 16u, 64u, (22u << 12u) | 0xfacu};
|
||||
input.resourcesDst[0] = {0u, 4u, 0u, 0u};
|
||||
const auto vertexKey = vertex.Key();
|
||||
const auto contextKey = RecompileCacheKey::ContextHash(vertex.request);
|
||||
input.resources[0].fields = {0x20000u, 32u << 16u, 128u, (77u << 12u) | 0xfa9u};
|
||||
require(vertex.Key() == vertexKey && RecompileCacheKey::ContextHash(vertex.request) == contextKey, "runtime vertex format, stride, swizzle or address changed the static interface key");
|
||||
input.resources[0].fields[3] = (20u << 12u) | 0xfacu;
|
||||
require(vertex.Key() != vertexKey && RecompileCacheKey::ContextHash(vertex.request) != contextKey, "integer vertex input reused a float interface");
|
||||
input.resources[0].fields[3] = (77u << 12u) | 0xfa9u;
|
||||
input.resourcesDst[0].registersNum = 2u;
|
||||
require(vertex.Key() != vertexKey, "vertex component interface did not change the artifact key");
|
||||
input.fetchEmbedded = true;
|
||||
const auto embeddedKey = vertex.Key();
|
||||
input.resources[0].fields = {};
|
||||
input.resourcesNum = 0u;
|
||||
require(vertex.Key() == embeddedKey, "runtime vertex fetch descriptors changed the artifact key");
|
||||
}
|
||||
|
||||
void verifyStore() {
|
||||
@@ -482,7 +494,8 @@ void verifyAcrossProcesses(const char* self) {
|
||||
struct ClockRequest {
|
||||
std::vector<std::uint32_t> code{0xf4900100u, 0x00000000u, 0xbf8cc07fu, 0x7e020204u, 0xe0700000u, 0x80000100u, 0xf800180fu, 0x01010101u, 0xbf810000u};
|
||||
std::array<std::uint32_t, 4> userData{0x10000000u, 0x00000000u, 0x40u, 0x00027facu};
|
||||
std::array<std::uint32_t, 1> capabilities{1u};
|
||||
std::array<std::uint32_t, 4> capabilities{1u, 11u, 5347u, 4448u};
|
||||
std::array<std::string_view, 2> extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"};
|
||||
RecompileRequest request{};
|
||||
|
||||
explicit ClockRequest(std::uint32_t stride = 0) {
|
||||
@@ -501,13 +514,15 @@ struct ClockRequest {
|
||||
request.target.spirvVersion = 0x00010300u;
|
||||
request.target.subgroupSize = 32;
|
||||
request.target.supportedCapabilities = capabilities;
|
||||
request.target.supportedExtensions = extensions;
|
||||
request.target.bdaAbiVersion = 1u;
|
||||
request.layout.pushConstantSizeBytes = 128;
|
||||
}
|
||||
};
|
||||
|
||||
std::string emissionFailure(const RecompileRequest& request) {
|
||||
try {
|
||||
static_cast<void>(Recompile(request));
|
||||
static_cast<void>(PrepareShader(request));
|
||||
} catch (const std::runtime_error& error) {
|
||||
const std::string message = error.what();
|
||||
require(message.find("reads the shader clock, which needs the device's VK_KHR_shader_clock") != std::string::npos, "unexpected failure: " + message.substr(0, message.find("RecompileRequest:")));
|
||||
@@ -534,12 +549,12 @@ void verifyEmissionFailureMemo() {
|
||||
require(diskMisses() == before.misses + 2, "another binding layout reused the failure");
|
||||
ClockRequest stride(16);
|
||||
static_cast<void>(emissionFailure(stride.request));
|
||||
require(diskMisses() == before.misses + 3, "another buffer specialization reused the failure");
|
||||
require(diskMisses() == before.misses + 2, "a runtime buffer stride change repeated static preparation");
|
||||
ClockRequest relocated;
|
||||
relocated.request.shader.codeAddress += 0x1000u;
|
||||
static_cast<void>(emissionFailure(relocated.request));
|
||||
require(diskMisses() == before.misses + 4, "a relocated copy reused the failure");
|
||||
require(emissionFailure(clock.request) == first && diskMisses() == before.misses + 4, "the other requests replaced the failure");
|
||||
require(diskMisses() == before.misses + 3, "a relocated copy reused the failure");
|
||||
require(emissionFailure(clock.request) == first && diskMisses() == before.misses + 3, "the other requests replaced the failure");
|
||||
|
||||
ClockRequest concurrent;
|
||||
concurrent.request.layout.pushConstantSizeBytes = 64;
|
||||
@@ -548,7 +563,7 @@ void verifyEmissionFailureMemo() {
|
||||
std::vector<std::string> messages;
|
||||
for (auto& future : failures) messages.push_back(future.get());
|
||||
require(std::all_of(messages.begin(), messages.end(), [&](const std::string& message) { return message == messages.front(); }), "concurrent requests failed with different messages");
|
||||
require(diskMisses() == before.misses + 5, "concurrent requests emitted the program " + std::to_string(diskMisses() - before.misses - 4) + " times");
|
||||
require(diskMisses() == before.misses + 4, "concurrent requests emitted the program " + std::to_string(diskMisses() - before.misses - 3) + " times");
|
||||
|
||||
ShaderDiskCache::Flush();
|
||||
require(ShaderDiskCache::Totals().writes == before.writes, "an emission failure was stored in the disk cache");
|
||||
|
||||
@@ -277,14 +277,12 @@ void verifyBindlessTable() {
|
||||
return std::vector<std::uint32_t>(snapshot.flattenedSrt.end() - static_cast<std::ptrdiff_t>(1u + 2u * slots), snapshot.flattenedSrt.end());
|
||||
};
|
||||
const auto tableRoot = [&](const ResourceCapture& capture, std::uint32_t direct) {
|
||||
require(capture.specialization.images.size() == direct + slots - 1u, "bindless: the specialization does not hold the table slots");
|
||||
require(capture.snapshot.images.size() == direct + slots - 1u, "bindless: the snapshot does not hold the table slots");
|
||||
std::uint32_t root = ImageResource::NoIndirectImage;
|
||||
for (std::uint32_t i = 0; i < direct; i++) {
|
||||
if (capture.specialization.images[i].indirectRoot == i) root = i;
|
||||
if (capture.plan->descriptorSources.at(capture.plan->info.images.at(i).source).indirectImage.has_value()) root = i;
|
||||
}
|
||||
require(root != ImageResource::NoIndirectImage, "bindless: no table root");
|
||||
for (std::uint32_t i = direct; i < capture.specialization.images.size(); i++) require(capture.specialization.images[i].indirectRoot == root, "bindless: an extra image is not the root's slot");
|
||||
return root;
|
||||
};
|
||||
|
||||
@@ -300,6 +298,16 @@ void verifyBindlessTable() {
|
||||
require(tables == 1, "bindless: the table source was not planned");
|
||||
for (const auto& image : plan->info.images) require(image.indirectSearchIterations == 0u, "bindless: the plan carries a search depth");
|
||||
const auto direct = static_cast<std::uint32_t>(plan->info.images.size());
|
||||
auto staticRequest = request;
|
||||
const std::array<std::uint32_t, 2> absentResources{};
|
||||
staticRequest.context.userData = absentResources;
|
||||
staticRequest.context.memory = {};
|
||||
auto staticProgram = PrepareResourceProgram(staticRequest);
|
||||
ResourceMaterializer{}.ApplyStaticInterface(staticProgram);
|
||||
const auto& staticImages = staticProgram.Resources().info.images;
|
||||
require(staticImages.size() == direct + slots - 1u, "bindless: the static interface needs runtime descriptors");
|
||||
const auto staticRoot = std::ranges::find_if(staticImages, [](const ImageResource& image) { return image.indirectSearchIterations != 0u; });
|
||||
require(staticRoot != staticImages.end() && staticRoot->indirectResources.size() == slots && staticRoot->indirectMappingOffset == plan->srtReads.size(), "bindless: the static table interface is incomplete");
|
||||
|
||||
AgcDriver::ShaderMemory memory({});
|
||||
const auto capture = memory.Capture(request);
|
||||
@@ -308,7 +316,7 @@ void verifyBindlessTable() {
|
||||
for (std::uint32_t i = direct + 2u; i < capture->snapshot.images.size(); i++) require(capture->snapshot.images[i].dwords == heap[2], "bindless: a pad slot is not null");
|
||||
const auto mapping = mappingOf(capture->snapshot);
|
||||
require(std::vector<std::uint32_t>(mapping.begin(), mapping.begin() + 7) == std::vector<std::uint32_t>{3u, 0u, 0u, 1u, 1u, 3u, 2u}, "bindless: the (key, slot) mapping is wrong");
|
||||
require(capture->specialization.images[root].indirectMappingOffset + mapping.size() == capture->snapshot.flattenedSrt.size(), "bindless: the mapping offset does not name the block");
|
||||
require(capture->plan->srtReads.size() + mapping.size() == capture->snapshot.flattenedSrt.size(), "bindless: the mapping offset does not name the block");
|
||||
auto regions = memory.Regions();
|
||||
for (const auto& material : materials) require(covered(regions, &material[1], sizeof(std::uint32_t)), "bindless: a material key was not captured");
|
||||
for (const auto entry : {0u, 1u, 3u}) require(covered(regions, heap[entry].data(), 32u), "bindless: a table entry was not captured");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define CORE_SHADER_RECOMPILER_CACHEKEY_HPP
|
||||
|
||||
#include "Recompiler.hpp"
|
||||
#include "RdnaDecoder/RdnaDescriptorFormat.hpp"
|
||||
#include "prx/libc/include/HostThreadLocal.hpp"
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
@@ -164,8 +165,7 @@ private:
|
||||
if (value.fetchEmbedded) return;
|
||||
append(key, value.resourcesNum);
|
||||
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, VertexInputNumericClass(static_cast<IrBufferFormat>((value.resources[i].fields[3] >> 12u) & 0x7fu)));
|
||||
append(key, value.resourcesDst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
#include "Recompiler.hpp"
|
||||
#include "IntermediateRepresentation/IrMetadata/CompiledShaderInfo.hpp"
|
||||
#include "Optimization/include/Optimization/BindingAllocator.hpp"
|
||||
#include "Optimization/include/Optimization/ResourceMaterializer.hpp"
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
struct CompiledVariant {
|
||||
ResourceSpecialization specialization;
|
||||
BindingLayout layout;
|
||||
CompiledShaderInfo info;
|
||||
CompiledBindingLayout bindings;
|
||||
|
||||
@@ -8,46 +8,18 @@
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
struct ResourceSpecialization {
|
||||
struct Image {
|
||||
IrTextureNumericClass numericClass = IrTextureNumericClass::Unsupported;
|
||||
RdnaImageDimension dimension = RdnaImageDimension::Unknown;
|
||||
std::uint32_t mipCount = 1;
|
||||
IrBufferFormat conversionFormat = IrBufferFormat::Invalid;
|
||||
std::uint32_t shaderSwizzle = ShaderImageIdentitySwizzle;
|
||||
std::uint32_t indirectRoot = ImageResource::NoIndirectImage;
|
||||
std::uint32_t indirectMappingOffset = 0;
|
||||
std::uint32_t indirectSearchIterations = 0;
|
||||
bool cube = false;
|
||||
bool fmask = false;
|
||||
bool depthBits = false;
|
||||
bool depthUnorm16 = false;
|
||||
IrBufferFormat packedFormat = IrBufferFormat::Invalid;
|
||||
std::uint32_t emulatedCompare = 0;
|
||||
bool srgbDecode = false;
|
||||
|
||||
bool operator==(const Image& other) const;
|
||||
};
|
||||
|
||||
std::vector<Image> images;
|
||||
|
||||
bool operator==(const ResourceSpecialization& other) const;
|
||||
|
||||
std::vector<std::uint32_t> boundDescriptors;
|
||||
};
|
||||
|
||||
// Why a bindless image table (a T# loaded from a table buffer at a runtime key) was not bound;
|
||||
// counted on the [bindless] line (APS5_PROFILE_DRAW).
|
||||
enum class BindlessRejection { Capacity, MaterialScan, NoEntry, Storage, NonUniform, ImageSlots, Count };
|
||||
|
||||
class ResourceMaterializer {
|
||||
public:
|
||||
void Apply(IrProgram& program, const ResourceSpecialization& specialization) const;
|
||||
static std::uint32_t EmulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index);
|
||||
void ApplyStaticInterface(IrProgram& program) const;
|
||||
static std::vector<ImageResource> RuntimeImageModes(const ImageResource& image);
|
||||
static std::uint32_t RuntimeImageMode(const ImageResource& image, const DescriptorValue& descriptor);
|
||||
[[nodiscard]] IrResourcePlan ExtractPlan(const IrProgram& program) const;
|
||||
void Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot, ResourceSpecialization& specialization) const;
|
||||
// APS5_PROFILE_DRAW: the time Materialize spent building specializations, over every call.
|
||||
void Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot) const;
|
||||
static std::uint64_t SpecializationNanoseconds();
|
||||
static std::uint32_t BindlessSlots();
|
||||
static void CountBindlessRejection(BindlessRejection reason);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "Recompiler.hpp"
|
||||
#include "IntermediateRepresentation/IrProgram.hpp"
|
||||
#include "Optimization/ResourceMaterializer.hpp"
|
||||
#include "Optimization/SrtWalker.hpp"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
@@ -12,13 +12,10 @@ namespace ShaderRecompiler {
|
||||
[[nodiscard]] IrProgram PrepareResourceProgram(const RecompileRequest& request);
|
||||
[[nodiscard]] std::shared_ptr<const IrResourcePlan> GetResourcePlan(const RecompileRequest& request);
|
||||
|
||||
// What a driver's capture of a request produces: the plan and the materialization of the words the
|
||||
// runtime read through it. Recompile(request, capture) compiles from these without a second walk.
|
||||
struct SourceEntry;
|
||||
struct ResourceCapture {
|
||||
std::shared_ptr<const IrResourcePlan> plan;
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
// The cache entry the plan belongs to; null when the request bypasses the cache.
|
||||
std::shared_ptr<SourceEntry> source;
|
||||
// The walk's read addresses when the plan has pure flat slots (the leaf of each pure slot,
|
||||
|
||||
@@ -491,8 +491,8 @@ std::uint32_t colorCompareReference(IrBufferFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSnapshot& snapshot, std::uint32_t index) {
|
||||
const auto& image = plan.info.images[index];
|
||||
std::uint32_t emulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index) {
|
||||
const auto& image = info.images[index];
|
||||
const auto& descriptor = snapshot.images[index];
|
||||
if (!image.depthCompare || descriptor.dwordCount != 8u || nullImageDescriptor(descriptor)) return 0u;
|
||||
const auto format = rawImageFormat(descriptor);
|
||||
@@ -502,7 +502,7 @@ std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSna
|
||||
if (type != ImageType::Color2D && type != ImageType::Color2DArray) throw std::runtime_error("comparison sampling of a color texture is implemented only for 2D and 2D array views");
|
||||
if ((descriptorImageSwizzle(descriptor) & 0x7u) != 4u) throw std::runtime_error("comparison sampling of a color texture is implemented only when the view's X channel is red");
|
||||
std::optional<std::uint32_t> samplerState;
|
||||
for (const auto& pair : plan.info.sampledPairs) {
|
||||
for (const auto& pair : info.sampledPairs) {
|
||||
if (pair.image != index) continue;
|
||||
if (pair.sampler >= snapshot.samplers.size() || snapshot.samplers[pair.sampler].dwordCount != 4u) throw std::runtime_error("comparison sampling of a color texture has no sampler descriptor");
|
||||
const auto& words = snapshot.samplers[pair.sampler].dwords;
|
||||
@@ -536,77 +536,33 @@ std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSna
|
||||
return *samplerState | (reference << EmulatedCompare::ReferenceShift) | (singleLevel ? EmulatedCompare::SingleLevel : 0u);
|
||||
}
|
||||
|
||||
void buildResourceSpecialization(const IrResourcePlan& plan, ResourceSnapshot& snapshot, const std::vector<TableResolution>& tables, ResourceSpecialization& specialization) {
|
||||
ResourceSpecialization result;
|
||||
for (std::uint32_t i = 0; i < plan.info.buffers.size(); ++i) {
|
||||
void materializeTables(const IrResourcePlan& plan, ResourceSnapshot& snapshot, const std::vector<TableResolution>& tables) {
|
||||
for (std::uint32_t i = 0u; i < plan.info.buffers.size(); ++i) {
|
||||
const auto decoded = decodeBufferDescriptor(snapshot.buffers.at(i));
|
||||
if (decoded.Type() != 0u) throw std::runtime_error("buffer descriptor uses an unsupported type");
|
||||
if (plan.stage != IrShaderStage::Compute && decoded.AddTid()) throw std::runtime_error("buffer ADD_TID is only valid for compute shaders");
|
||||
}
|
||||
|
||||
result.images.reserve(plan.info.images.size());
|
||||
for (std::uint32_t i = 0; i < plan.info.images.size(); i++) {
|
||||
if (snapshot.flattenedSrt.size() != plan.srtReads.size()) throw std::runtime_error("runtime SRT size differs from the static interface");
|
||||
for (std::uint32_t i = 0u; i < plan.info.images.size(); ++i) {
|
||||
const auto& image = plan.info.images[i];
|
||||
const DecodedImage decoded = decodeImageDescriptor(snapshot.images[i], image, plan.srgbDecodeFormats);
|
||||
if (decoded.fmask && std::any_of(plan.info.sampledPairs.begin(), plan.info.sampledPairs.end(), [i](const SampledResourcePair& pair) { return pair.image == i; })) {
|
||||
throw std::runtime_error("FMASK requires a direct image load");
|
||||
}
|
||||
ResourceSpecialization::Image entry;
|
||||
entry.numericClass = image.atomic ? IrTextureNumericClass::Uint : IrTextureNumericClass::Float;
|
||||
entry.dimension = image.dimension;
|
||||
entry.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u;
|
||||
entry.conversionFormat = IrBufferFormat::Invalid;
|
||||
entry.shaderSwizzle = ShaderImageIdentitySwizzle;
|
||||
entry.indirectRoot = ImageResource::NoIndirectImage;
|
||||
entry.indirectMappingOffset = 0u;
|
||||
entry.indirectSearchIterations = 0u;
|
||||
entry.cube = false;
|
||||
entry.fmask = false;
|
||||
entry.depthBits = false;
|
||||
entry.depthUnorm16 = false;
|
||||
entry.packedFormat = IrBufferFormat::Invalid;
|
||||
entry.emulatedCompare = emulatedCompareState(plan, snapshot, i);
|
||||
entry.srgbDecode = decoded.srgbDecode;
|
||||
result.images.push_back(entry);
|
||||
}
|
||||
|
||||
// A table root is followed by its slots 1..C-1 as extra images of the root's shape; the
|
||||
// (key, slot) mapping the SPIR-V selector searches is appended to the flattened SRT in a
|
||||
// block of fixed size, so the offsets (part of the specialization) never depend on the keys.
|
||||
for (std::uint32_t i = 0; i < plan.info.images.size(); i++) {
|
||||
const auto& table = tables[i];
|
||||
if (table.slots.empty()) {
|
||||
static_cast<void>(ResourceMaterializer::RuntimeImageMode(image, snapshot.images.at(i)));
|
||||
const auto& table = tables.at(i);
|
||||
if (!plan.descriptorSources.at(image.source).indirectImage.has_value()) {
|
||||
if (!table.slots.empty()) throw std::runtime_error("direct image has runtime table slots");
|
||||
continue;
|
||||
}
|
||||
const auto mappingOffset = static_cast<std::uint32_t>(snapshot.flattenedSrt.size());
|
||||
const auto slots = ResourceMaterializer::BindlessSlots();
|
||||
if (table.slots.size() != slots) throw std::runtime_error("runtime table size differs from the static interface");
|
||||
const auto mappingOffset = snapshot.flattenedSrt.size();
|
||||
snapshot.flattenedSrt.push_back(static_cast<std::uint32_t>(table.mapping.size()));
|
||||
for (const auto& [key, slot] : table.mapping) {
|
||||
if (slot >= slots) throw std::runtime_error("runtime table slot exceeds its static capacity");
|
||||
snapshot.flattenedSrt.push_back(key);
|
||||
snapshot.flattenedSrt.push_back(slot == 0u ? i : static_cast<std::uint32_t>(snapshot.images.size()) + slot - 1u);
|
||||
}
|
||||
snapshot.flattenedSrt.resize(mappingOffset + 1u + 2u * table.slots.size(), 0u);
|
||||
auto root = result.images[i];
|
||||
root.indirectRoot = i;
|
||||
root.indirectMappingOffset = mappingOffset;
|
||||
root.indirectSearchIterations = static_cast<std::uint32_t>(std::bit_width(table.slots.size()));
|
||||
result.images[i] = root;
|
||||
root.indirectMappingOffset = 0u;
|
||||
root.indirectSearchIterations = 0u;
|
||||
for (std::uint32_t slot = 1; slot < table.slots.size(); slot++) {
|
||||
result.images.push_back(root);
|
||||
snapshot.images.push_back(table.slots[slot]);
|
||||
}
|
||||
snapshot.flattenedSrt.resize(mappingOffset + 1u + 2u * slots, 0u);
|
||||
snapshot.images.insert(snapshot.images.end(), table.slots.begin() + 1u, table.slots.end());
|
||||
}
|
||||
|
||||
result.boundDescriptors.clear();
|
||||
result.boundDescriptors.reserve(plan.info.buffers.size() + result.images.size());
|
||||
for (std::uint32_t index = 0; index < plan.info.buffers.size(); index++) {
|
||||
result.boundDescriptors.push_back(index);
|
||||
}
|
||||
for (std::uint32_t index = 0; index < result.images.size(); index++) {
|
||||
result.boundDescriptors.push_back(index);
|
||||
}
|
||||
specialization = std::move(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -694,60 +650,44 @@ std::uint32_t ResourceMaterializer::RuntimeImageMode(const ImageResource& image,
|
||||
throw std::runtime_error("image descriptor is incompatible with the static runtime image interface");
|
||||
}
|
||||
|
||||
void ResourceMaterializer::Apply(IrProgram& program, const ResourceSpecialization& specialization) const {
|
||||
IrResourcePlan& resources = program.Resources();
|
||||
if (!resources.resourceTrackingComplete) {
|
||||
throw std::runtime_error("ResourceMaterializer::Apply requires a completed resource plan");
|
||||
}
|
||||
if (resources.info.images.size() > specialization.images.size()) {
|
||||
throw std::runtime_error("ResourceMaterializer::Apply image count mismatch");
|
||||
}
|
||||
std::uint32_t ResourceMaterializer::EmulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index) {
|
||||
return emulatedCompareState(info, snapshot, index);
|
||||
}
|
||||
|
||||
void ResourceMaterializer::ApplyStaticInterface(IrProgram& program) const {
|
||||
auto& resources = program.Resources();
|
||||
if (!resources.resourceTrackingComplete || !resources.srtPlanComplete) throw std::runtime_error("static resource interface requires a completed resource plan");
|
||||
auto images = resources.info.images;
|
||||
images.reserve(specialization.images.size());
|
||||
for (std::uint32_t index = 0; index < specialization.images.size(); index++) {
|
||||
const auto& source = specialization.images[index];
|
||||
if (index >= images.size()) {
|
||||
if (source.indirectRoot >= resources.info.images.size()) {
|
||||
throw std::runtime_error("ResourceMaterializer::Apply indirect image root is out of range");
|
||||
const auto directCount = static_cast<std::uint32_t>(images.size());
|
||||
auto mappingOffset = static_cast<std::uint32_t>(resources.srtReads.size());
|
||||
const auto slots = BindlessSlots();
|
||||
for (std::uint32_t index = 0u; index < directCount; ++index) {
|
||||
auto image = images[index];
|
||||
if (image.indirectRoot != ImageResource::NoIndirectImage) throw std::runtime_error("static image interface was already expanded");
|
||||
image.numericClass = image.atomic ? IrTextureNumericClass::Uint : IrTextureNumericClass::Float;
|
||||
image.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u;
|
||||
if (resources.descriptorSources.at(image.source).indirectImage.has_value()) {
|
||||
if (images.size() + slots - 1u > ShaderInfo::MaxImages) throw std::runtime_error("static bindless image capacity exceeded");
|
||||
image.indirectRoot = index;
|
||||
image.indirectResources.push_back(index);
|
||||
for (std::uint32_t slot = 1u; slot < slots; ++slot) {
|
||||
auto entry = image;
|
||||
entry.indirectResources.clear();
|
||||
image.indirectResources.push_back(static_cast<std::uint32_t>(images.size()));
|
||||
images.push_back(std::move(entry));
|
||||
}
|
||||
images.push_back(resources.info.images[source.indirectRoot]);
|
||||
image.indirectMappingOffset = mappingOffset;
|
||||
image.indirectSearchIterations = static_cast<std::uint32_t>(std::bit_width(slots));
|
||||
mappingOffset += 1u + 2u * slots;
|
||||
}
|
||||
auto& image = images[index];
|
||||
image.numericClass = source.numericClass;
|
||||
image.dimension = source.dimension;
|
||||
image.mipCount = source.mipCount;
|
||||
image.conversionFormat = source.conversionFormat;
|
||||
image.shaderSwizzle = source.shaderSwizzle;
|
||||
image.indirectRoot = source.indirectRoot;
|
||||
image.indirectMappingOffset = source.indirectMappingOffset;
|
||||
image.indirectSearchIterations = source.indirectSearchIterations;
|
||||
image.cube = source.cube;
|
||||
image.depthBits = source.depthBits;
|
||||
image.depthUnorm16 = source.depthUnorm16;
|
||||
image.packedFormat = source.packedFormat;
|
||||
image.emulatedCompare = source.emulatedCompare;
|
||||
image.srgbDecode = source.srgbDecode;
|
||||
if ((source.emulatedCompare & EmulatedCompare::Enabled) != 0u) image.depthCompare = false;
|
||||
image.indirectResources.clear();
|
||||
images[index] = std::move(image);
|
||||
}
|
||||
for (std::uint32_t index = 0; index < images.size(); index++) {
|
||||
const auto root = images[index].indirectRoot;
|
||||
if (root != ImageResource::NoIndirectImage) {
|
||||
if (root >= images.size()) {
|
||||
throw std::runtime_error("ResourceMaterializer::Apply indirect image root is out of range");
|
||||
}
|
||||
images[root].indirectResources.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
auto samplers = resources.info.samplers;
|
||||
for (const auto& pair : resources.info.sampledPairs) {
|
||||
if (pair.image >= images.size() || pair.sampler >= samplers.size()) throw std::runtime_error("runtime sampled pair is out of range");
|
||||
samplers[pair.sampler].depthCompare = samplers[pair.sampler].depthCompare || images[pair.image].depthCompare;
|
||||
if (pair.image >= images.size() || pair.sampler >= resources.info.samplers.size()) throw std::runtime_error("static sampled pair is out of range");
|
||||
auto& sampler = resources.info.samplers[pair.sampler];
|
||||
sampler.depthCompare = sampler.depthCompare || images[pair.image].depthCompare;
|
||||
}
|
||||
resources.info.images = std::move(images);
|
||||
resources.info.samplers = std::move(samplers);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -856,7 +796,7 @@ IrResourcePlan ResourceMaterializer::ExtractPlan(const IrProgram& program) const
|
||||
return plan;
|
||||
}
|
||||
|
||||
void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot, ResourceSpecialization& specialization) const {
|
||||
void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot) const {
|
||||
const IrResourcePlan& plan = program;
|
||||
if (!plan.resourceTrackingComplete) {
|
||||
throw std::runtime_error("ResourceMaterializer::Materialize requires a completed resource plan");
|
||||
@@ -873,12 +813,10 @@ void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtR
|
||||
reportBindless();
|
||||
throw;
|
||||
}
|
||||
ResourceSpecialization nextSpecialization;
|
||||
const auto started = MaterializeProfiled() ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
|
||||
buildResourceSpecialization(plan, nextSnapshot, tables, nextSpecialization);
|
||||
materializeTables(plan, nextSnapshot, tables);
|
||||
if (MaterializeProfiled()) specializationNanoseconds.fetch_add(static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - started).count()), std::memory_order_relaxed);
|
||||
snapshot = std::move(nextSnapshot);
|
||||
specialization = std::move(nextSpecialization);
|
||||
reportBindless();
|
||||
}
|
||||
|
||||
@@ -894,12 +832,4 @@ void ResourceMaterializer::CountBindlessRejection(BindlessRejection reason) {
|
||||
if (reason < BindlessRejection::Count) bindlessCounters().rejected[static_cast<std::size_t>(reason)].fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool ResourceSpecialization::Image::operator==(const Image& other) const {
|
||||
return numericClass == other.numericClass && dimension == other.dimension && mipCount == other.mipCount && conversionFormat == other.conversionFormat && shaderSwizzle == other.shaderSwizzle && indirectRoot == other.indirectRoot && indirectMappingOffset == other.indirectMappingOffset && indirectSearchIterations == other.indirectSearchIterations && cube == other.cube && fmask == other.fmask && depthBits == other.depthBits && depthUnorm16 == other.depthUnorm16 && packedFormat == other.packedFormat && emulatedCompare == other.emulatedCompare && srgbDecode == other.srgbDecode;
|
||||
}
|
||||
|
||||
bool ResourceSpecialization::operator==(const ResourceSpecialization& other) const {
|
||||
return images == other.images;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ enum class ImageType : std::uint32_t {
|
||||
Color2DMsaaArray = 15
|
||||
};
|
||||
|
||||
[[nodiscard]] IrTextureNumericClass VertexInputNumericClass(IrBufferFormat format);
|
||||
[[nodiscard]] bool IsFmaskTextureFormat(IrBufferFormat format);
|
||||
[[nodiscard]] IrTextureNumericClass SampledTextureNumericClass(IrBufferFormat format);
|
||||
[[nodiscard]] IrBufferFormat RemapTextureFormat(IrBufferFormat format);
|
||||
|
||||
@@ -137,4 +137,30 @@ bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3) {
|
||||
return DepthBitsTextureWidth(word1, word3) != 0u;
|
||||
}
|
||||
|
||||
IrTextureNumericClass VertexInputNumericClass(IrBufferFormat format) {
|
||||
switch (format) {
|
||||
case IrBufferFormat::Format8UInt:
|
||||
case IrBufferFormat::Format16UInt:
|
||||
case IrBufferFormat::Format8_8UInt:
|
||||
case IrBufferFormat::Format32UInt:
|
||||
case IrBufferFormat::Format16_16UInt:
|
||||
case IrBufferFormat::Format8_8_8_8UInt:
|
||||
case IrBufferFormat::Format32_32UInt:
|
||||
case IrBufferFormat::Format16_16_16_16UInt:
|
||||
case IrBufferFormat::Format32_32_32UInt:
|
||||
case IrBufferFormat::Format32_32_32_32UInt: return IrTextureNumericClass::Uint;
|
||||
case IrBufferFormat::Format8SInt:
|
||||
case IrBufferFormat::Format16SInt:
|
||||
case IrBufferFormat::Format8_8SInt:
|
||||
case IrBufferFormat::Format32SInt:
|
||||
case IrBufferFormat::Format16_16SInt:
|
||||
case IrBufferFormat::Format8_8_8_8SInt:
|
||||
case IrBufferFormat::Format32_32SInt:
|
||||
case IrBufferFormat::Format16_16_16_16SInt:
|
||||
case IrBufferFormat::Format32_32_32SInt:
|
||||
case IrBufferFormat::Format32_32_32_32SInt: return IrTextureNumericClass::Sint;
|
||||
default: return IrTextureNumericClass::Float;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,7 +181,6 @@ struct ResultMemoEntry {
|
||||
struct EmissionFailure {
|
||||
std::uint64_t codeAddress;
|
||||
BindingLayout layout;
|
||||
ResourceSpecialization specialization;
|
||||
std::exception_ptr failure;
|
||||
};
|
||||
|
||||
@@ -240,6 +239,7 @@ std::shared_ptr<SourceEntry> getSource(const RecompileRequest& request) {
|
||||
struct SourceKeyStorage {};
|
||||
auto& key = HostThreadLocal<std::vector<std::uint64_t>, SourceKeyStorage>();
|
||||
RecompileCacheKey::Build(request, key);
|
||||
key.push_back(HostSubgroupSize(request));
|
||||
const auto find = [&]() -> std::shared_ptr<SourceEntry> {
|
||||
const auto found = sources.find(key);
|
||||
if (found == sources.end()) return nullptr;
|
||||
@@ -297,11 +297,11 @@ std::uint64_t nextVariantId() {
|
||||
return variants.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, const ResourceSnapshot& resourceSnapshot, const ResourceSpecialization& resourceSpecialization, std::exception_ptr* emissionFailure = nullptr) {
|
||||
CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, std::exception_ptr* emissionFailure = nullptr) {
|
||||
const auto inputInfo = RequestInputInfo(request);
|
||||
constexpr DeadCodeEliminator deadCodeEliminator;
|
||||
constexpr ResourceMaterializer resourceMaterializer;
|
||||
resourceMaterializer.Apply(program, resourceSpecialization);
|
||||
resourceMaterializer.ApplyStaticInterface(program);
|
||||
|
||||
deadCodeEliminator.RemoveIdentities(program);
|
||||
deadCodeEliminator.Eliminate(program);
|
||||
@@ -312,9 +312,6 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
||||
constexpr BindingAllocator bindingAllocator;
|
||||
auto bindings = bindingAllocator.Allocate(program, request.layout);
|
||||
|
||||
constexpr DescriptorBindingBuilder descriptorBindingBuilder;
|
||||
descriptorBindingBuilder.Populate(bindings, program, resourceSnapshot, partialThreads(request));
|
||||
|
||||
SpirvTargetOptions targetOptions {};
|
||||
targetOptions.vulkanVersion = request.target.vulkanVersion;
|
||||
targetOptions.spirvVersion = request.target.spirvVersion;
|
||||
@@ -362,7 +359,7 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
||||
}
|
||||
}
|
||||
|
||||
return {resourceSpecialization, request.layout, std::move(program).TakeCompiledInfo(), std::move(static_cast<CompiledBindingLayout&>(bindings)), std::move(result)};
|
||||
return {request.layout, std::move(program).TakeCompiledInfo(), std::move(static_cast<CompiledBindingLayout&>(bindings)), std::move(result)};
|
||||
}
|
||||
|
||||
RecompileResult materializeResult(const CompiledVariant& variant, const RecompileRequest& request, const ResourceSnapshot& snapshot) {
|
||||
@@ -387,25 +384,24 @@ 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;
|
||||
}
|
||||
|
||||
std::shared_ptr<const CompiledVariant> findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization, bool& cacheHit) {
|
||||
std::shared_ptr<const CompiledVariant> findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, bool& cacheHit) {
|
||||
for (const auto& candidate : source.variants) {
|
||||
if (sameLayout(candidate->layout, request.layout) && candidate->specialization == specialization) {
|
||||
if (sameLayout(candidate->layout, request.layout)) {
|
||||
cacheHit = true;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
for (const auto& failure : source.emissionFailures) {
|
||||
if (failure.codeAddress == request.shader.codeAddress && sameLayout(failure.layout, request.layout) && failure.specialization == specialization) std::rethrow_exception(failure.failure);
|
||||
if (failure.codeAddress == request.shader.codeAddress && sameLayout(failure.layout, request.layout)) std::rethrow_exception(failure.failure);
|
||||
}
|
||||
cacheHit = false;
|
||||
const bool disk = ShaderDiskCache::Enabled() && !DebugProbeActive();
|
||||
std::vector<std::byte> diskKey;
|
||||
std::shared_ptr<const CompiledVariant> variant;
|
||||
if (disk) {
|
||||
ShaderDiskCache::BuildKey(request, HostSubgroupSize(request), specialization, diskKey);
|
||||
ShaderDiskCache::BuildKey(request, HostSubgroupSize(request), diskKey);
|
||||
CompiledVariant loaded;
|
||||
if (ShaderDiskCache::Load(diskKey, loaded)) {
|
||||
loaded.specialization = specialization;
|
||||
loaded.layout = request.layout;
|
||||
loaded.artifact.variantId = nextVariantId();
|
||||
variant = std::make_shared<const CompiledVariant>(std::move(loaded));
|
||||
@@ -416,9 +412,9 @@ std::shared_ptr<const CompiledVariant> findOrCompileVariant(SourceEntry& source,
|
||||
source.program.reset();
|
||||
std::exception_ptr emissionFailure;
|
||||
try {
|
||||
variant = std::make_shared<const CompiledVariant>(compileVariant(request, std::move(program), snapshot, specialization, &emissionFailure));
|
||||
variant = std::make_shared<const CompiledVariant>(compileVariant(request, std::move(program), &emissionFailure));
|
||||
} catch (...) {
|
||||
if (emissionFailure != nullptr && FailureMemo()) source.emissionFailures.push_back({request.shader.codeAddress, request.layout, specialization, emissionFailure});
|
||||
if (emissionFailure != nullptr && FailureMemo()) source.emissionFailures.push_back({request.shader.codeAddress, request.layout, emissionFailure});
|
||||
throw;
|
||||
}
|
||||
if (disk) ShaderDiskCache::Store(std::move(diskKey), variant);
|
||||
@@ -428,13 +424,12 @@ std::shared_ptr<const CompiledVariant> findOrCompileVariant(SourceEntry& source,
|
||||
return variant;
|
||||
}
|
||||
|
||||
// The cached variant of `source` for the specialization, compiled on first use.
|
||||
RecompileResult materializeVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization) {
|
||||
RecompileResult materializeVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot) {
|
||||
std::shared_ptr<const CompiledVariant> variant;
|
||||
bool cacheHit = false;
|
||||
{
|
||||
std::lock_guard lock(source.mutex);
|
||||
variant = findOrCompileVariant(source, request, snapshot, specialization, cacheHit);
|
||||
variant = findOrCompileVariant(source, request, cacheHit);
|
||||
}
|
||||
auto result = materializeResult(*variant, request, snapshot);
|
||||
result.cacheHit = cacheHit;
|
||||
@@ -446,18 +441,17 @@ RecompileResult RecompileImpl(const RecompileRequest& request) {
|
||||
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);
|
||||
materializer.Materialize(plan, runtime, snapshot);
|
||||
const auto variant = compileVariant(request, std::move(program));
|
||||
return materializeResult(variant, request, snapshot);
|
||||
}
|
||||
const auto source = getSource(request);
|
||||
materializer.Materialize(*source->plan, runtime, snapshot, specialization);
|
||||
return materializeVariant(*source, request, snapshot, specialization);
|
||||
materializer.Materialize(*source->plan, runtime, snapshot);
|
||||
return materializeVariant(*source, request, snapshot);
|
||||
}
|
||||
|
||||
// APS5_NO_RESULT_MEMO=1: every Recompile(request, capture) materializes its own result as before.
|
||||
@@ -543,7 +537,7 @@ std::uint64_t snapshotHash(const RecompileRequest& request, const ResourceSnapsh
|
||||
// The memo'd result of `source`'s variant for the snapshot (design13 R5): a hit returns the shared
|
||||
// object, a miss materializes outside the source mutex and inserts (a concurrent miss's object is
|
||||
// as good). `memoHit` reports the hit.
|
||||
std::shared_ptr<const RecompileResult> materializeMemoized(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization, bool* memoHit) {
|
||||
std::shared_ptr<const RecompileResult> materializeMemoized(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, bool* memoHit) {
|
||||
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
|
||||
std::shared_ptr<const CompiledVariant> variant;
|
||||
bool cacheHit = false;
|
||||
@@ -552,7 +546,7 @@ std::shared_ptr<const RecompileResult> materializeMemoized(SourceEntry& source,
|
||||
auto& counters = resultMemoCounters();
|
||||
{
|
||||
std::lock_guard lock(source.mutex);
|
||||
variant = findOrCompileVariant(source, request, snapshot, specialization, cacheHit);
|
||||
variant = findOrCompileVariant(source, request, cacheHit);
|
||||
index = (variant->artifact.variantId * 0x9e3779b97f4a7c15ull) ^ hash;
|
||||
const auto found = source.memoIndex.find(index);
|
||||
if (found != source.memoIndex.end() && found->second->variantId == variant->artifact.variantId && found->second->hash == hash) {
|
||||
@@ -601,11 +595,11 @@ std::shared_ptr<const RecompileResult> materializeMemoized(SourceEntry& source,
|
||||
std::shared_ptr<const RecompileResult> RecompileImpl(const RecompileRequest& request, const ResourceCapture& capture, bool* memoHit) {
|
||||
if (!request.useCache || capture.source == nullptr) {
|
||||
auto program = PrepareResourceProgram(request);
|
||||
const auto variant = compileVariant(request, std::move(program), capture.snapshot, capture.specialization);
|
||||
const auto variant = compileVariant(request, std::move(program));
|
||||
return std::make_shared<const RecompileResult>(materializeResult(variant, request, capture.snapshot));
|
||||
}
|
||||
if (!ResultMemo()) return std::make_shared<const RecompileResult>(materializeVariant(*capture.source, request, capture.snapshot, capture.specialization));
|
||||
return materializeMemoized(*capture.source, request, capture.snapshot, capture.specialization, memoHit);
|
||||
if (!ResultMemo()) return std::make_shared<const RecompileResult>(materializeVariant(*capture.source, request, capture.snapshot));
|
||||
return materializeMemoized(*capture.source, request, capture.snapshot, memoHit);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
@@ -636,12 +630,12 @@ namespace {
|
||||
void materializeCapture(ResourceCapture& capture, const SrtRuntime& runtime) {
|
||||
const auto& plan = *capture.plan;
|
||||
if (std::none_of(plan.pureFlatSlots.begin(), plan.pureFlatSlots.end(), [](std::uint8_t pure) { return pure != 0u; })) {
|
||||
ResourceMaterializer{}.Materialize(plan, runtime, capture.snapshot, capture.specialization);
|
||||
ResourceMaterializer{}.Materialize(plan, runtime, capture.snapshot);
|
||||
return;
|
||||
}
|
||||
SrtRuntime traced = runtime;
|
||||
traced.readTrace = &capture.readTrace;
|
||||
ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot, capture.specialization);
|
||||
ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot);
|
||||
auto& other = capture.readTrace.otherReads;
|
||||
std::sort(other.begin(), other.end());
|
||||
other.erase(std::unique(other.begin(), other.end()), other.end());
|
||||
|
||||
@@ -438,12 +438,6 @@ struct RecompileResult : CompiledShaderArtifact, ShaderInvocation {
|
||||
|
||||
[[nodiscard]] RecompileResult Recompile(const RecompileRequest& request);
|
||||
|
||||
// The resource plan, snapshot and specialization a driver captured for the request (see
|
||||
// CaptureResources in Optimization/ResourceProgram.hpp): this overload reuses them instead of
|
||||
// materializing the request's memory regions again, and is otherwise Recompile(request). The
|
||||
// result is immutable and shared: a capture that reproduces a snapshot the source's variant was
|
||||
// materialized over before receives the same object (`memoHit`), so the descriptor population runs
|
||||
// once per distinct snapshot. APS5_NO_RESULT_MEMO=1 materializes every call.
|
||||
struct ResourceCapture;
|
||||
[[nodiscard]] std::shared_ptr<const RecompileResult> Recompile(const RecompileRequest& request, const ResourceCapture& capture, bool* memoHit = nullptr);
|
||||
|
||||
|
||||
@@ -82,8 +82,6 @@ static_assert(sizeof(IrBindingLayout) == 64, "IrBindingLayout changed: update th
|
||||
static_assert(sizeof(IrDescriptorBinding) == 32, "IrDescriptorBinding changed: update the layout encoder");
|
||||
static_assert(sizeof(BindingAllocationResult) == 120, "BindingAllocationResult changed: update the allocation encoder");
|
||||
static_assert(sizeof(CompiledBindingLayout) == 72, "CompiledBindingLayout changed: update the allocation encoder");
|
||||
static_assert(sizeof(ResourceSpecialization) == 48, "ResourceSpecialization changed: update BuildKey");
|
||||
static_assert(sizeof(ResourceSpecialization::Image) == 48, "ResourceSpecialization::Image changed: update BuildKey");
|
||||
static_assert(sizeof(BindingLayout) == 16, "BindingLayout changed: update BuildKey");
|
||||
#endif
|
||||
|
||||
@@ -802,7 +800,7 @@ std::uint64_t SourceVersion() {
|
||||
return Generated::SourceVersion;
|
||||
}
|
||||
|
||||
void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, const ResourceSpecialization& specialization, std::vector<std::byte>& key) {
|
||||
void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, std::vector<std::byte>& key) {
|
||||
key.clear();
|
||||
Writer writer(key);
|
||||
writer.Value(FileMagic);
|
||||
@@ -818,24 +816,6 @@ void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, c
|
||||
writer.Value(request.layout.firstBinding);
|
||||
writer.Value(request.layout.pushConstantOffsetBytes);
|
||||
writer.Value(request.layout.pushConstantSizeBytes);
|
||||
writer.List(specialization.images, [](Writer& out, const ResourceSpecialization::Image& image) {
|
||||
out.Value(image.numericClass);
|
||||
out.Value(image.dimension);
|
||||
out.Value(image.mipCount);
|
||||
out.Value(image.conversionFormat);
|
||||
out.Value(image.shaderSwizzle);
|
||||
out.Value(image.indirectRoot);
|
||||
out.Value(image.indirectMappingOffset);
|
||||
out.Value(image.indirectSearchIterations);
|
||||
out.Value(image.cube);
|
||||
out.Value(image.fmask);
|
||||
out.Value(image.depthBits);
|
||||
out.Value(image.depthUnorm16);
|
||||
out.Value(image.packedFormat);
|
||||
out.Value(image.emulatedCompare);
|
||||
out.Value(image.srgbDecode);
|
||||
});
|
||||
writer.Values(std::span<const std::uint32_t>(specialization.boundDescriptors));
|
||||
const auto& switches = switchKey();
|
||||
key.insert(key.end(), switches.begin(), switches.end());
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace ShaderRecompiler::ShaderDiskCache {
|
||||
|
||||
inline constexpr std::uint32_t FormatVersion = 10;
|
||||
inline constexpr std::uint32_t FormatVersion = 11;
|
||||
|
||||
enum class LoadStatus {
|
||||
Loaded,
|
||||
@@ -33,7 +33,7 @@ struct Counters {
|
||||
|
||||
[[nodiscard]] std::uint64_t SourceVersion();
|
||||
|
||||
void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, const ResourceSpecialization& specialization, std::vector<std::byte>& key);
|
||||
void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, std::vector<std::byte>& key);
|
||||
|
||||
[[nodiscard]] std::string EntryName(std::span<const std::byte> key);
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ struct SpirvTargetOptions {
|
||||
|
||||
class SpirvEmitter {
|
||||
public:
|
||||
[[nodiscard]] std::vector<std::uint32_t> Emit(const IrProgram& program, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const;
|
||||
[[nodiscard]] std::vector<std::uint32_t> Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const;
|
||||
[[nodiscard]] std::vector<std::uint32_t> Emit(const IrProgram& program, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const;
|
||||
[[nodiscard]] std::vector<std::uint32_t> Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ std::uint32_t ConstantBool(SpirvEmitterState& state, bool value);
|
||||
std::uint32_t ConstantU64(SpirvEmitterState& state, std::uint64_t value);
|
||||
std::uint32_t ConstantU32CompositeZero(SpirvEmitterState& state, std::uint32_t components);
|
||||
std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name);
|
||||
void CheckBindings(const IrProgram& program, const BindingAllocationResult& bindings);
|
||||
void CheckBindings(const IrProgram& program, const CompiledBindingLayout& bindings);
|
||||
void EmitBaseHeader(SpirvModule& module, const IrProgram& program);
|
||||
void DefineInputs(SpirvEmitterState& state);
|
||||
void DefineOutputs(SpirvEmitterState& state);
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings);
|
||||
void DefineModule(SpirvEmitterState& state);
|
||||
std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name);
|
||||
std::uint32_t ExecutionModelForStage(IrShaderStage stage);
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings);
|
||||
void DefineModule(SpirvEmitterState& state);
|
||||
std::uint32_t ExecutionModelForStage(IrShaderStage stage);
|
||||
std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name);
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings);
|
||||
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -229,11 +229,11 @@ std::uint32_t SpirvValueEmitContext::Label(const IrBlock* block) const {
|
||||
throw std::runtime_error("SPIR-V emission failed: hash=0x" + std::to_string(state.program.Resources().shaderHash) + " stage=" + std::to_string(static_cast<unsigned>(state.program.Resources().stage)) + " opcode=" + std::string(IrOpcodeName(inst.Opcode())) + " reason=" + reason);
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const {
|
||||
std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const {
|
||||
return Emit(program, ShaderStageInputInfo {}, bindings, target);
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const {
|
||||
std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const {
|
||||
if (program.Resources().stage != IrShaderStage::Compute && program.Resources().stage != IrShaderStage::Vertex && program.Resources().stage != IrShaderStage::Pixel && program.Resources().stage != IrShaderStage::Mesh && program.Resources().stage != IrShaderStage::Local && program.Resources().stage != IrShaderStage::TessellationControl && program.Resources().stage != IrShaderStage::TessellationEvaluation) {
|
||||
FailProgram(program, "binary SPIR-V emitter received an unsupported shader stage");
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ std::uint32_t PushConstantBlockType(SpirvEmitterState& state) {
|
||||
|
||||
}
|
||||
|
||||
void CheckBindings(const IrProgram& program, const BindingAllocationResult& bindings) {
|
||||
void CheckBindings(const IrProgram& program, const CompiledBindingLayout& bindings) {
|
||||
const IrProgramMetadata& metadata = program.Metadata();
|
||||
if (!metadata.bindingLayoutComplete) {
|
||||
FailEmit("shader binding layout has not been allocated for this program");
|
||||
@@ -133,59 +133,6 @@ void CheckBindings(const IrProgram& program, const BindingAllocationResult& bind
|
||||
if (!(bindings.layout == metadata.bindings)) {
|
||||
FailEmit("binding allocation result does not match the program's committed binding layout");
|
||||
}
|
||||
if (bindings.bindings.size() != bindings.layout.descriptors.size()) {
|
||||
FailEmit("binding allocation result has a different descriptor count than the binding layout");
|
||||
}
|
||||
const IrShaderStage stage = program.Resources().stage;
|
||||
for (std::size_t index = 0; index < bindings.layout.descriptors.size(); index++) {
|
||||
const IrDescriptorBinding& logical = bindings.layout.descriptors[index];
|
||||
const DescriptorBinding& physical = bindings.bindings[index];
|
||||
const std::uint32_t expectedCount = logical.resources.empty() ? 1u : static_cast<std::uint32_t>(logical.resources.size());
|
||||
if (physical.count != expectedCount) {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor count");
|
||||
}
|
||||
if (physical.descriptorSet != 0u) {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " is bound to the wrong descriptor set");
|
||||
}
|
||||
if (physical.binding != NativeBinding(stage, logical.kind)) {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " is bound to the wrong native binding slot");
|
||||
}
|
||||
DescriptorKind expectedKind = DescriptorKind::StorageBuffer;
|
||||
DescriptorRole expectedRole = DescriptorRole::GuestBuffers;
|
||||
if (logical.kind == DescriptorBindingKind::Samplers) {
|
||||
expectedKind = DescriptorKind::Sampler;
|
||||
expectedRole = DescriptorRole::GuestSamplers;
|
||||
} else if (logical.kind == DescriptorBindingKind::Gds) {
|
||||
expectedRole = DescriptorRole::Gds;
|
||||
} else if (logical.kind == DescriptorBindingKind::BdaPagetable) {
|
||||
expectedRole = DescriptorRole::BdaPagetable;
|
||||
} else if (logical.kind == DescriptorBindingKind::FaultBuffer) {
|
||||
expectedRole = DescriptorRole::FaultBuffer;
|
||||
} else if (logical.kind == DescriptorBindingKind::FlattenedSrt) {
|
||||
expectedRole = DescriptorRole::FlattenedSrt;
|
||||
} else if (logical.kind == DescriptorBindingKind::ShaderData) {
|
||||
expectedRole = DescriptorRole::ShaderData;
|
||||
} else {
|
||||
const auto imageClass = ImageBindingResourceClass(logical.kind);
|
||||
if (imageClass == ImageResourceClass::Sampled) {
|
||||
expectedKind = DescriptorKind::SampledImage;
|
||||
expectedRole = DescriptorRole::GuestImages;
|
||||
} else if (imageClass == ImageResourceClass::Storage) {
|
||||
expectedKind = DescriptorKind::StorageImage;
|
||||
expectedRole = DescriptorRole::GuestImages;
|
||||
} else if (logical.kind == DescriptorBindingKind::Buffers) {
|
||||
expectedRole = DescriptorRole::GuestBuffers;
|
||||
} else {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " has an unmapped binding kind");
|
||||
}
|
||||
}
|
||||
if (physical.kind != expectedKind) {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor kind");
|
||||
}
|
||||
if (physical.role != expectedRole) {
|
||||
FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor role");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBaseHeader(SpirvModule& module, const IrProgram& program) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "RdnaDecoder/RdnaDescriptorFormat.hpp"
|
||||
#include "SpirvBackend/SpirvMemory/SpirvInputOutput.hpp"
|
||||
#include "SpirvBackend/SpirvEmitterHelpers.hpp"
|
||||
#include "SpirvBackend/SpirvMemory/SpirvTypes.hpp"
|
||||
@@ -88,29 +89,10 @@ namespace ShaderRecompiler
|
||||
if (location >= static_cast<std::uint32_t>(ShaderVertexInputInfo::MaxResources) || location >= static_cast<std::uint32_t>(vertex.resourcesNum)) {
|
||||
return VertexInputScalarKind::Float;
|
||||
}
|
||||
switch (vertex.resources[location].Format()) {
|
||||
case IrBufferFormat::Format8UInt:
|
||||
case IrBufferFormat::Format16UInt:
|
||||
case IrBufferFormat::Format8_8UInt:
|
||||
case IrBufferFormat::Format32UInt:
|
||||
case IrBufferFormat::Format16_16UInt:
|
||||
case IrBufferFormat::Format8_8_8_8UInt:
|
||||
case IrBufferFormat::Format32_32UInt:
|
||||
case IrBufferFormat::Format16_16_16_16UInt:
|
||||
case IrBufferFormat::Format32_32_32UInt:
|
||||
case IrBufferFormat::Format32_32_32_32UInt: return VertexInputScalarKind::Uint;
|
||||
case IrBufferFormat::Format8SInt:
|
||||
case IrBufferFormat::Format16SInt:
|
||||
case IrBufferFormat::Format8_8SInt:
|
||||
case IrBufferFormat::Format32SInt:
|
||||
case IrBufferFormat::Format16_16SInt:
|
||||
case IrBufferFormat::Format8_8_8_8SInt:
|
||||
case IrBufferFormat::Format32_32SInt:
|
||||
case IrBufferFormat::Format16_16_16_16SInt:
|
||||
case IrBufferFormat::Format32_32_32SInt:
|
||||
case IrBufferFormat::Format32_32_32_32SInt: return VertexInputScalarKind::Sint;
|
||||
default: return VertexInputScalarKind::Float;
|
||||
}
|
||||
const auto numeric = VertexInputNumericClass(vertex.resources[location].Format());
|
||||
if (numeric == IrTextureNumericClass::Uint) return VertexInputScalarKind::Uint;
|
||||
if (numeric == IrTextureNumericClass::Sint) return VertexInputScalarKind::Sint;
|
||||
return VertexInputScalarKind::Float;
|
||||
}
|
||||
|
||||
std::uint32_t VertexParameterComponentCount(const SpirvInputBinding& input) {
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
namespace ShaderRecompiler
|
||||
{
|
||||
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings) {
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings) {
|
||||
CheckBindings(program, bindings);
|
||||
EmitBaseHeader(module, program);
|
||||
}
|
||||
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings) {
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings) {
|
||||
CheckBindings(state.program, bindings);
|
||||
DefineModule(state);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user