mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
graphics: keep bounded image heaps resident in a bindless descriptor set
This commit is contained in:
@@ -161,6 +161,7 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) {
|
||||
if (!kind.has_value()) {
|
||||
Fail(program, "native shader plan has an invalid image class");
|
||||
}
|
||||
if (image.heap) continue;
|
||||
if (image.mip_count == 0u ||
|
||||
(image.mip_mode != IR::ImageMipMode::Dynamic && image.mip_count != 1u)) {
|
||||
Fail(program, "native shader plan has an invalid image mip descriptor count");
|
||||
|
||||
@@ -120,6 +120,10 @@ uint32_t ImageType(EmitterState& state, const IR::ImageResource& image) {
|
||||
} else {
|
||||
EXIT("invalid image resource class");
|
||||
}
|
||||
if (image.dimension == ImageDimension::Dim1D || image.dimension == ImageDimension::Dim1DArray) {
|
||||
state.builder.RequireCapability(sampled == 1 ? spv::CapabilitySampled1D
|
||||
: spv::CapabilityImage1D);
|
||||
}
|
||||
const auto& info = ImageDimensionInfoFor(image.dimension);
|
||||
if (sampled == 2 && info.multisampled != 0u) {
|
||||
state.builder.RequireCapability(spv::CapabilityStorageImageMultisample);
|
||||
@@ -147,14 +151,20 @@ void DecorateNonUniformDescriptor(EmitterState& state, uint32_t id, spv::Capabil
|
||||
state.builder.RequireCapability(capability);
|
||||
state.builder.AddAnnotation(spv::OpDecorate, id, spv::DecorationNonUniform);
|
||||
}
|
||||
|
||||
ImageDefinition& ImageDescriptorDefinition(EmitterState& state, const IR::ImageResource& image,
|
||||
IR::DescriptorBindingKind kind) {
|
||||
return (image.heap ? state.heap_images : state.images)[IR::ImageBindingIndex(kind)];
|
||||
}
|
||||
} // namespace
|
||||
|
||||
uint32_t LoadImageDescriptor(EmitterState& state, uint32_t resource, uint32_t mip,
|
||||
uint32_t array_index) {
|
||||
const auto pointer = ImageDescriptorPointer(state, resource, mip, array_index);
|
||||
const auto image = state.builder.AllocateId();
|
||||
const auto kind = *IR::DescriptorBindingForImage(state.program.info.images[resource]);
|
||||
state.builder.AddFunction(spv::OpLoad, state.images[IR::ImageBindingIndex(kind)].type,
|
||||
const auto& resource_info = state.program.info.images[resource];
|
||||
const auto kind = *IR::DescriptorBindingForImage(resource_info);
|
||||
state.builder.AddFunction(spv::OpLoad, ImageDescriptorDefinition(state, resource_info, kind).type,
|
||||
image, pointer);
|
||||
if (array_index != 0u) {
|
||||
DecorateNonUniformDescriptor(state, image,
|
||||
@@ -187,13 +197,13 @@ uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampl
|
||||
EXIT_IF(image_resource.resource_class != IR::ImageResourceClass::Sampled);
|
||||
const auto image = LoadImageDescriptor(state, resource, mip, array_index);
|
||||
const auto sampled_image = state.builder.AllocateId();
|
||||
const auto kind = *IR::DescriptorBindingForImage(image_resource);
|
||||
auto& sampled_type = state.images[IR::ImageBindingIndex(kind)].sampled_type;
|
||||
if (sampled_type == 0) {
|
||||
sampled_type = state.builder.Type(
|
||||
spv::OpTypeSampledImage, state.images[IR::ImageBindingIndex(kind)].type);
|
||||
auto& definition = ImageDescriptorDefinition(state, image_resource,
|
||||
*IR::DescriptorBindingForImage(image_resource));
|
||||
if (definition.sampled_type == 0) {
|
||||
definition.sampled_type = state.builder.Type(spv::OpTypeSampledImage, definition.type);
|
||||
}
|
||||
state.builder.AddFunction(spv::OpSampledImage, sampled_type, sampled_image, image, sampler_id);
|
||||
state.builder.AddFunction(spv::OpSampledImage, definition.sampled_type, sampled_image, image,
|
||||
sampler_id);
|
||||
if (array_index != 0u || sampler_dynamic) {
|
||||
DecorateNonUniformDescriptor(state, sampled_image, spv::CapabilitySampledImageArrayNonUniformIndexing);
|
||||
}
|
||||
@@ -205,11 +215,11 @@ uint32_t ImageDescriptorPointer(EmitterState& state, uint32_t resource, uint32_t
|
||||
const auto& image = state.program.info.images.at(resource);
|
||||
EXIT_IF(mip >= image.mip_count);
|
||||
const auto kind = IR::DescriptorBindingForImage(image);
|
||||
EXIT_IF(!kind.has_value());
|
||||
EXIT_IF(!kind.has_value() || (image.heap && array_index == 0u));
|
||||
if (array_index == 0u) {
|
||||
array_index = ConstantU32(state, image.descriptor_index + mip);
|
||||
}
|
||||
const auto& definition = state.images[IR::ImageBindingIndex(*kind)];
|
||||
const auto& definition = ImageDescriptorDefinition(state, image, *kind);
|
||||
return DescriptorElementPointer(state, definition.pointer_type, definition.variable,
|
||||
array_index, *kind, resource,
|
||||
"image descriptor array was not emitted");
|
||||
|
||||
@@ -700,6 +700,21 @@ auto EmitImageAccess(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t resul
|
||||
image.indirect_resources.size() < 2u) {
|
||||
ctx.Fail(inst, "has no indirect image runtime mapping");
|
||||
}
|
||||
if (image.heap) {
|
||||
const auto entry = EmitImageHeapEntry(state, key, image.indirect_mapping_offset);
|
||||
const auto kinds = static_cast<uint32_t>(image.indirect_resources.size()) - 1u;
|
||||
// One kind leaves every slot word a bare element.
|
||||
if (kinds == 1u) return emit(image.indirect_resources[1], entry);
|
||||
const auto element = Binary(state, spv::OpBitwiseAnd, TypeU32(state), entry,
|
||||
ConstantU32(state, (1u << IR::ImageHeapKindShift) - 1u));
|
||||
const auto EmitKind = [&](uint32_t kind) {
|
||||
return emit(image.indirect_resources[kind + 1u], element);
|
||||
};
|
||||
return EmitIndexSwitch(state,
|
||||
Binary(state, spv::OpShiftRightLogical, TypeU32(state), entry,
|
||||
ConstantU32(state, IR::ImageHeapKindShift)),
|
||||
kinds, result_type, EmitKind);
|
||||
}
|
||||
const auto selected = EmitIndirectResourceIndex(
|
||||
state, key, image.indirect_mapping_offset, image.indirect_search_iterations, 0u);
|
||||
const auto SameType = [&](uint32_t lhs, uint32_t rhs) {
|
||||
@@ -879,14 +894,19 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
}
|
||||
if (op == IR::ValueOpcode::ImageQueryLod) {
|
||||
state.builder.RequireCapability(spv::CapabilityImageQuery);
|
||||
const auto dimension = image.dimension;
|
||||
const auto sampled = MakeSampledImage(state, mem.resource,
|
||||
LoadSamplerDescriptor(state, mem.sampler));
|
||||
const auto lod = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
spv::OpImageQueryLod, TypeF32Vector(state, 2), lod, sampled,
|
||||
CoordF32(ctx, mem, *address, 0, ImageDimensionInfoFor(dimension).spatial_components,
|
||||
image.cube));
|
||||
const auto sampler = LoadSamplerDescriptor(state, mem.sampler);
|
||||
const auto lod = EmitImageAccess(ctx, inst, TypeF32Vector(state, 2),
|
||||
[&](uint32_t resource, uint32_t array_index) {
|
||||
const auto& candidate = state.program.info.images[resource];
|
||||
const auto query = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
spv::OpImageQueryLod, TypeF32Vector(state, 2), query,
|
||||
MakeSampledImage(state, resource, sampler, 0u, array_index),
|
||||
CoordF32(ctx, mem, *address, 0,
|
||||
ImageDimensionInfoFor(candidate.dimension).spatial_components,
|
||||
candidate.cube));
|
||||
return query;
|
||||
});
|
||||
uint32_t values[4] = {ConstantU32(state, 0), ConstantU32(state, 0), ConstantU32(state, 0),
|
||||
ConstantU32(state, 0)};
|
||||
for (uint32_t index = 0; index < 2u; index++) {
|
||||
|
||||
@@ -130,6 +130,7 @@ struct EmitterState {
|
||||
uint32_t lds_u64_variable = 0;
|
||||
std::array<uint32_t, 2> scratch_variable {};
|
||||
std::array<ImageDefinition, IR::ImageBindingCount> images {};
|
||||
std::array<ImageDefinition, IR::ImageBindingCount> heap_images {};
|
||||
uint32_t sampler_variable = 0;
|
||||
uint32_t sampler_type = 0;
|
||||
uint32_t sampler_pointer_type = 0;
|
||||
@@ -431,6 +432,8 @@ struct MemoryResourceAccess {
|
||||
|
||||
uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t mapping_offset,
|
||||
uint32_t search_iterations, uint32_t default_resource);
|
||||
// Slot word of an image heap byte offset; offsets past the heap select the null element.
|
||||
uint32_t EmitImageHeapEntry(EmitterState& state, uint32_t key, uint32_t mapping_offset);
|
||||
MemoryResourceAccess PrepareMemoryResourceAccess(EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t array_index = 0u);
|
||||
|
||||
|
||||
@@ -38,19 +38,39 @@ uint32_t EmitBinaryU32(EmitterState& state, spv::Op opcode, uint32_t lhs, uint32
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t LoadFlattenedSrt(EmitterState& state, uint32_t index) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain, TypeStorageBufferElementPointer(state), pointer,
|
||||
state.flattened_srt_variable, ConstantU32(state, 0), index);
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeU32(state), value, pointer);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t EmitImageHeapEntry(EmitterState& state, uint32_t key, uint32_t mapping_offset) {
|
||||
const auto Mapping = [&](uint32_t word) {
|
||||
return LoadFlattenedSrt(state, ConstantU32(state, mapping_offset + word));
|
||||
};
|
||||
// Keys past the table select its trailing null slot.
|
||||
const auto index = EmitUMin32(state, Binary(state, spv::OpShiftRightLogical, TypeU32(state), key,
|
||||
ConstantU32(state, 2u)),
|
||||
Mapping(0u));
|
||||
const auto offset = Unary(state, spv::OpUConvert, TypeU64(state),
|
||||
Binary(state, spv::OpShiftLeftLogical, TypeU32(state), index,
|
||||
ConstantU32(state, 2u)));
|
||||
const auto pointer = Unary(state, spv::OpConvertUToPtr, TypePhysicalU32Pointer(state),
|
||||
Binary(state, spv::OpIAdd, TypeU64(state),
|
||||
PackU64(state, Mapping(1u), Mapping(2u)), offset));
|
||||
const auto entry = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeU32(state), entry, pointer,
|
||||
spv::MemoryAccessAlignedMask, 4u);
|
||||
return entry;
|
||||
}
|
||||
|
||||
uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t mapping_offset,
|
||||
uint32_t search_iterations, uint32_t default_resource) {
|
||||
const auto LoadMapping = [&](uint32_t index) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain, TypeStorageBufferElementPointer(state),
|
||||
pointer, state.flattened_srt_variable, ConstantU32(state, 0),
|
||||
index);
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeU32(state), value, pointer);
|
||||
return value;
|
||||
};
|
||||
const auto mapping = ConstantU32(state, mapping_offset);
|
||||
const auto count = LoadMapping(mapping);
|
||||
const auto count = LoadFlattenedSrt(state, mapping);
|
||||
if (search_iterations == 0u) {
|
||||
const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), key,
|
||||
ConstantU32(state, 2u));
|
||||
@@ -58,7 +78,7 @@ uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t m
|
||||
const auto safe = Select(state, TypeU32(state), valid, index, ConstantU32(state, 0u));
|
||||
const auto entry = Binary(state, spv::OpIAdd, TypeU32(state), safe,
|
||||
ConstantU32(state, mapping_offset + 1u));
|
||||
return Select(state, TypeU32(state), valid, LoadMapping(entry),
|
||||
return Select(state, TypeU32(state), valid, LoadFlattenedSrt(state, entry),
|
||||
ConstantU32(state, default_resource));
|
||||
}
|
||||
const auto entry_at = [&](uint32_t index) {
|
||||
@@ -76,7 +96,7 @@ uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t m
|
||||
ConstantU32(state, 1u));
|
||||
const auto probe = Select(state, TypeU32(state), active, mid, ConstantU32(state, 0u));
|
||||
const auto less = Binary(state, spv::OpULessThan, TypeBool(state),
|
||||
LoadMapping(entry_at(probe)), key);
|
||||
LoadFlattenedSrt(state, entry_at(probe)), key);
|
||||
low = Select(state, TypeU32(state),
|
||||
Binary(state, spv::OpLogicalAnd, TypeBool(state), active, less),
|
||||
Binary(state, spv::OpIAdd, TypeU32(state), mid, ConstantU32(state, 1u)), low);
|
||||
@@ -87,9 +107,10 @@ uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t m
|
||||
const auto in_range = Binary(state, spv::OpULessThan, TypeBool(state), low, count);
|
||||
const auto entry = entry_at(Select(state, TypeU32(state), in_range, low, ConstantU32(state, 0u)));
|
||||
const auto match = Binary(state, spv::OpLogicalAnd, TypeBool(state), in_range,
|
||||
Binary(state, spv::OpIEqual, TypeBool(state), LoadMapping(entry), key));
|
||||
const auto resource = LoadMapping(Binary(state, spv::OpIAdd, TypeU32(state), entry,
|
||||
ConstantU32(state, 1u)));
|
||||
Binary(state, spv::OpIEqual, TypeBool(state),
|
||||
LoadFlattenedSrt(state, entry), key));
|
||||
const auto resource = LoadFlattenedSrt(
|
||||
state, Binary(state, spv::OpIAdd, TypeU32(state), entry, ConstantU32(state, 1u)));
|
||||
return Select(state, TypeU32(state), match, resource, ConstantU32(state, default_resource));
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,17 @@ uint32_t F32ArrayType(EmitterState& state, uint32_t count) {
|
||||
return state.builder.Type(spv::OpTypeArray, TypeF32(state), ConstantU32(state, count));
|
||||
}
|
||||
|
||||
void DefineImageArray(EmitterState& state, ImageDefinition& definition, const IR::ImageResource& image,
|
||||
uint32_t count, uint32_t set, uint32_t binding) {
|
||||
definition.type = ImageType(state, image);
|
||||
const auto array = state.builder.Type(spv::OpTypeArray, definition.type, ConstantU32(state, count));
|
||||
definition.variable = state.builder.DefineGlobalVariable(
|
||||
TypePointer(state, spv::StorageClassUniformConstant, array), spv::StorageClassUniformConstant);
|
||||
state.builder.AddAnnotation(spv::OpDecorate, definition.variable, spv::DecorationDescriptorSet, set);
|
||||
state.builder.AddAnnotation(spv::OpDecorate, definition.variable, spv::DecorationBinding, binding);
|
||||
definition.pointer_type = TypePointer(state, spv::StorageClassUniformConstant, definition.type);
|
||||
}
|
||||
|
||||
void DefineDescriptors(EmitterState& state) {
|
||||
using Kind = IR::DescriptorBindingKind;
|
||||
auto& info = state.program.info;
|
||||
@@ -222,6 +233,14 @@ void DefineDescriptors(EmitterState& state) {
|
||||
(image.mip_mode != IR::ImageMipMode::Dynamic && image.mip_count != 1u)) {
|
||||
EXIT("shader image has an invalid descriptor type or mip count");
|
||||
}
|
||||
if (image.heap) {
|
||||
// Heap kinds alias one array; the root itself is never accessed.
|
||||
auto& definition = state.heap_images[IR::ImageBindingIndex(*kind)];
|
||||
if (image.indirect_resources.empty() && definition.variable == 0) {
|
||||
DefineImageArray(state, definition, image, IR::ImageHeapCapacity, IR::ImageHeapSet, 0u);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto& count = counts[static_cast<size_t>(*kind)];
|
||||
image.descriptor_index = count;
|
||||
if (count == 0) image_types[IR::ImageBindingIndex(*kind)] = ℑ
|
||||
@@ -331,21 +350,11 @@ void DefineDescriptors(EmitterState& state) {
|
||||
default: {
|
||||
EXIT_IF(IR::ImageBindingResourceClass(kind) ==
|
||||
IR::ImageResourceClass::None);
|
||||
const auto& image = *image_types[IR::ImageBindingIndex(kind)];
|
||||
const auto name = "image_" + std::to_string(static_cast<uint32_t>(kind));
|
||||
auto& definition = state.images[IR::ImageBindingIndex(kind)];
|
||||
definition.type = ImageType(state, image);
|
||||
definition.variable = Define(ArrayType(definition.type), name.c_str(),
|
||||
spv::StorageClassUniformConstant);
|
||||
definition.pointer_type = TypePointer(
|
||||
state, spv::StorageClassUniformConstant, definition.type);
|
||||
if (image.dimension == ImageDimension::Dim1D ||
|
||||
image.dimension == ImageDimension::Dim1DArray) {
|
||||
state.builder.RequireCapability(image.resource_class ==
|
||||
IR::ImageResourceClass::Sampled
|
||||
? spv::CapabilitySampled1D
|
||||
: spv::CapabilityImage1D);
|
||||
}
|
||||
DefineImageArray(state, definition, *image_types[IR::ImageBindingIndex(kind)],
|
||||
counts[index], 0u, IR::NativeBinding(state.program.stage, kind));
|
||||
state.builder.AddName(definition.variable,
|
||||
("image_" + std::to_string(static_cast<uint32_t>(kind))).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -752,7 +761,7 @@ void DefineModule(EmitterState& state) {
|
||||
if (state.program.info.buffer_u16) {
|
||||
state.builder.RequireCapability(spv::CapabilityStorageBuffer16BitAccess);
|
||||
}
|
||||
if (state.program.info.uses_dma) {
|
||||
if (state.program.info.uses_dma || state.program.info.uses_image_heap) {
|
||||
state.builder.RequireCapability(spv::CapabilityInt64);
|
||||
state.builder.RequireCapability(spv::CapabilityPhysicalStorageBufferAddresses);
|
||||
state.builder.RequireExtension("SPV_KHR_physical_storage_buffer");
|
||||
@@ -820,7 +829,7 @@ void DefineModule(EmitterState& state) {
|
||||
state.builder.RequireExtension("SPV_KHR_fragment_shader_barycentric");
|
||||
}
|
||||
state.builder.RequireExtension("SPV_KHR_float_controls");
|
||||
state.builder.AddMemoryModel(state.program.info.uses_dma
|
||||
state.builder.AddMemoryModel(state.program.info.uses_dma || state.program.info.uses_image_heap
|
||||
? spv::AddressingModelPhysicalStorageBuffer64
|
||||
: spv::AddressingModelLogical,
|
||||
spv::MemoryModelGLSL450);
|
||||
|
||||
Reference in New Issue
Block a user