mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
implement dynamic
This commit is contained in:
@@ -113,6 +113,13 @@ inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_fo
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
const bool supported_mip =
|
||||
(resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
|
||||
resource.mip_levels == 1u) ||
|
||||
(resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage &&
|
||||
resource.mip_levels > 0u &&
|
||||
resource.mip_levels <= ShaderRecompiler::IR::ImageResource::MaxMipLevels &&
|
||||
!resource.read && !resource.atomic);
|
||||
return (resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
@@ -120,7 +127,7 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.written &&
|
||||
supported_mip && resource.written &&
|
||||
(!resource.atomic ||
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.read)) &&
|
||||
|
||||
@@ -94,10 +94,11 @@ vk::DescriptorBufferInfo BufferInfo(const BufferView& view) {
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::DescriptorImageInfo DescriptorCache::MakeImageInfo(const TextureBinding& texture) {
|
||||
EXIT_IF(!texture.image_id || texture.image_view == nullptr ||
|
||||
texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, texture.image_view, texture.layout};
|
||||
vk::DescriptorImageInfo DescriptorCache::MakeImageInfo(const TextureBinding& texture,
|
||||
uint32_t mip) {
|
||||
const auto view = texture.mip_views.empty() ? texture.image_view : texture.mip_views.at(mip);
|
||||
EXIT_IF(!texture.image_id || view == nullptr || texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, view, texture.layout};
|
||||
}
|
||||
|
||||
DescriptorCache::~DescriptorCache() {
|
||||
@@ -154,7 +155,8 @@ void DescriptorCache::CreatePool() {
|
||||
MaxSets * (ShaderRecompiler::IR::ShaderInfo::MaxBuffers +
|
||||
ShaderRecompiler::IR::ShaderInfo::MaxAddresses + 3u)},
|
||||
{vk::DescriptorType::eSampledImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eStorageImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eStorageImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages *
|
||||
ShaderRecompiler::IR::ImageResource::MaxMipLevels},
|
||||
{vk::DescriptorType::eSampler, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxSamplers},
|
||||
};
|
||||
vk::DescriptorPoolCreateInfo info {};
|
||||
@@ -229,8 +231,10 @@ VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage
|
||||
auto* set = Allocate(stage, program);
|
||||
EXIT_NOT_IMPLEMENTED(set == nullptr);
|
||||
|
||||
const auto descriptor_count = program.info.buffers.size() + program.info.images.size() +
|
||||
program.info.samplers.size() + program.info.addresses.size() + 3u;
|
||||
uint32_t descriptor_count = 0;
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
descriptor_count += DescriptorCount(binding);
|
||||
}
|
||||
std::vector<vk::DescriptorBufferInfo> buffer_infos;
|
||||
std::vector<vk::DescriptorImageInfo> image_infos;
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
@@ -238,6 +242,7 @@ VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage
|
||||
image_infos.reserve(descriptor_count);
|
||||
writes.reserve(program.bindings.descriptors.size());
|
||||
|
||||
std::vector<uint32_t> image_mips(program.info.images.size());
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
vk::WriteDescriptorSet write {};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
@@ -273,7 +278,11 @@ VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage
|
||||
default: {
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& texture = data.images.at(resource);
|
||||
image_infos.push_back(MakeImageInfo(texture));
|
||||
const auto mip = program.info.images.at(resource).mip_mode ==
|
||||
ShaderRecompiler::IR::ImageMipMode::DynamicStorage
|
||||
? image_mips.at(resource)++
|
||||
: 0u;
|
||||
image_infos.push_back(MakeImageInfo(texture, mip));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -47,10 +47,11 @@ public:
|
||||
enum class Stage { Unknown, Vertex, Pixel, Compute };
|
||||
|
||||
struct TextureBinding {
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||
std::vector<vk::ImageView> mip_views;
|
||||
};
|
||||
|
||||
struct NativeDescriptors {
|
||||
@@ -94,7 +95,7 @@ private:
|
||||
int next_free_pool = -1;
|
||||
};
|
||||
|
||||
static vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture);
|
||||
static vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture, uint32_t mip = 0);
|
||||
void CreatePool();
|
||||
VulkanDescriptorSet* Allocate(Stage stage, const ShaderRecompiler::IR::Program& program);
|
||||
vk::DescriptorSetLayout
|
||||
|
||||
@@ -391,9 +391,16 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(swizzle) &&
|
||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||
const auto base_level = static_cast<uint32_t>(descriptor.BaseLevel());
|
||||
const auto last_level = static_cast<uint32_t>(descriptor.LastLevel());
|
||||
const auto mip_levels = last_level >= base_level ? last_level - base_level + 1u : 0u;
|
||||
const bool dynamic_mip =
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
|
||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
||||
supported_mip_view && descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
supported_mip_view && mip_levels != 0u &&
|
||||
((dynamic_mip && mip_levels == resource.mip_levels) ||
|
||||
(!dynamic_mip && descriptor.BaseLevel() == descriptor.LastLevel())) &&
|
||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
||||
}
|
||||
@@ -618,14 +625,15 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
const bool multisampled = IsMultisampledTexture(type);
|
||||
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
const bool msaa_tile =
|
||||
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
|
||||
(multisampled &&
|
||||
(base_level != 0 || last_level == 0 || last_level > 3 ||
|
||||
descriptor.MaxMip() != last_level || !msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
||||
descriptor.MaxMip() != last_level || !msaa_tile ||
|
||||
(descriptor.MsaaDepth() && !depth_tile) ||
|
||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||
@@ -634,11 +642,15 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6],
|
||||
descriptor.fields[7]);
|
||||
}
|
||||
const auto samples = multisampled ? 1u << last_level : 1u;
|
||||
const auto view_levels =
|
||||
multisampled ? 1u : static_cast<uint32_t>(last_level - base_level) + 1u;
|
||||
multisampled ||
|
||||
(storage && resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage)
|
||||
? 1u
|
||||
: static_cast<uint32_t>(last_level - base_level) + 1u;
|
||||
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
|
||||
const auto format = descriptor.Format();
|
||||
const bool sampled_numeric_class =
|
||||
@@ -661,8 +673,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
TileSizeAlign size {};
|
||||
if (multisampled) {
|
||||
const auto bytes = Prospero::NumBytesPerElement(format);
|
||||
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
||||
: TileGetRenderTargetPitch(width, bytes, last_level);
|
||||
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
||||
: TileGetRenderTargetPitch(width, bytes, last_level);
|
||||
if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) ||
|
||||
size.size > UINT32_MAX / image_layers) {
|
||||
EXIT("unsupported multisample texture layout\n");
|
||||
@@ -679,8 +691,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
ValidateStorageTexture(resource, descriptor, size.size);
|
||||
}
|
||||
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format =
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
|
||||
? vk::Format::eR32Uint
|
||||
: SrgbStorageViewFormat(pixel_format);
|
||||
@@ -893,7 +905,17 @@ void RenderExecutor::RebindImages(CommandBuffer& buffer,
|
||||
}
|
||||
auto& binding = images[i];
|
||||
binding.image_view = texture_cache.FindTexture(binding.image_id, binding.desc);
|
||||
auto& image = texture_cache.GetImage(binding.image_id);
|
||||
auto& image = texture_cache.GetImage(binding.image_id);
|
||||
binding.mip_views.clear();
|
||||
if (program.info.images[i].mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage) {
|
||||
binding.mip_views.reserve(program.info.images[i].mip_levels);
|
||||
for (uint32_t mip = 0; mip < program.info.images[i].mip_levels; mip++) {
|
||||
auto view = binding.desc.view_info;
|
||||
view.base_level += mip;
|
||||
view.level_count = 1;
|
||||
binding.mip_views.push_back(mip == 0 ? binding.image_view : image.FindView(view));
|
||||
}
|
||||
}
|
||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||
image.usage.storage |= storage;
|
||||
image.usage.texture |= !storage;
|
||||
@@ -971,7 +993,11 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
auto& image = m_context.GetTextureCache().GetImage(descriptors.images[i].image_id);
|
||||
auto& binding = descriptors.images[i];
|
||||
const auto& view = binding.desc.view_info;
|
||||
const ImageSubresourceRange range {view.base_level, view.level_count, view.base_layer,
|
||||
const auto level_count =
|
||||
program.info.images[i].mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage
|
||||
? program.info.images[i].mip_levels
|
||||
: view.level_count;
|
||||
const ImageSubresourceRange range {view.base_level, level_count, view.base_layer,
|
||||
view.layer_count};
|
||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||
if (image.info.data.Empty()) {
|
||||
|
||||
@@ -287,6 +287,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
||||
LOGF("shaderStorageImageReadWithoutFormat is not supported\n");
|
||||
skip_device = true;
|
||||
}
|
||||
if (features12.shaderStorageImageArrayNonUniformIndexing != VK_TRUE) {
|
||||
LOGF("shaderStorageImageArrayNonUniformIndexing is not supported\n");
|
||||
skip_device = true;
|
||||
}
|
||||
|
||||
if (device_features2.features.shaderImageGatherExtended != VK_TRUE) {
|
||||
LOGF("shaderImageGatherExtended is not supported\n");
|
||||
@@ -514,6 +518,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features;
|
||||
features12.pNext = &depth_clip_control;
|
||||
features12.samplerMirrorClampToEdge = VK_TRUE;
|
||||
features12.shaderStorageImageArrayNonUniformIndexing = VK_TRUE;
|
||||
|
||||
vk::PhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control {};
|
||||
subgroup_size_control.sType = vk::StructureType::ePhysicalDeviceSubgroupSizeControlFeatures;
|
||||
|
||||
@@ -237,8 +237,14 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
if (!ImageBinding(program.info.images[i], kind)) {
|
||||
return Fail(error, "native shader plan has an invalid image class");
|
||||
}
|
||||
const auto bindings = program.info.images[i].NumBindings();
|
||||
if (bindings == 0 || bindings > IR::ImageResource::MaxMipLevels) {
|
||||
return Fail(error, "native shader plan has an invalid image descriptor count");
|
||||
}
|
||||
present[static_cast<size_t>(kind)] = true;
|
||||
expected[static_cast<size_t>(kind)].push_back(i);
|
||||
for (uint32_t binding = 0; binding < bindings; binding++) {
|
||||
expected[static_cast<size_t>(kind)].push_back(i);
|
||||
}
|
||||
}
|
||||
if (!program.info.samplers.empty()) {
|
||||
Expect(Kind::Samplers, Dense(program.info.samplers.size()));
|
||||
@@ -316,6 +322,11 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
inst.memory.image_dimension)) {
|
||||
return Fail(error, "image instruction has an invalid dense resource");
|
||||
}
|
||||
if (inst.op == IR::Opcode::ImageStore &&
|
||||
((program.info.images[inst.memory.resource].mip_mode ==
|
||||
IR::ImageMipMode::DynamicStorage) != inst.memory.image_has_mip)) {
|
||||
return Fail(error, "storage image mip mode does not match the instruction");
|
||||
}
|
||||
const bool address =
|
||||
inst.op == IR::Opcode::SLoadDword || memory == IR::ResourceKind::Flat ||
|
||||
memory == IR::ResourceKind::Global || memory == IR::ResourceKind::Scratch;
|
||||
|
||||
@@ -560,12 +560,21 @@ uint32_t DescriptorElementPointer(EmitterState& state, uint32_t result_ptr_type,
|
||||
uint32_t variable_id, uint32_t array_index,
|
||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
||||
const char* variable_name) {
|
||||
return DescriptorElementPointerId(state, result_ptr_type, variable_id,
|
||||
ConstantU32(state, array_index), kind, resource,
|
||||
variable_name);
|
||||
}
|
||||
|
||||
uint32_t DescriptorElementPointerId(EmitterState& state, uint32_t result_ptr_type,
|
||||
uint32_t variable_id, uint32_t array_index_id,
|
||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
||||
const char* variable_name) {
|
||||
if (variable_id == 0) {
|
||||
ExitDescriptorBindingFailure(state, kind, resource, variable_name);
|
||||
}
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpAccessChain, result_ptr_type, pointer, variable_id, ConstantU32(state, array_index)});
|
||||
{OpAccessChain, result_ptr_type, pointer, variable_id, array_index_id});
|
||||
return pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ uint32_t ConstantImageGatherHorizontalOffsets(EmitterState& state, ImageViewKind
|
||||
}
|
||||
|
||||
uint32_t LoadStorageImageDescriptorAtIndex(EmitterState& state, uint32_t resource,
|
||||
uint32_t array_index, bool uint_image,
|
||||
uint32_t array_index_id, bool uint_image,
|
||||
ImageViewKind view) {
|
||||
const auto kind = StorageBindingKind(uint_image, view);
|
||||
const auto& descriptors = state.storage_images[StorageImageIndex(uint_image, view)];
|
||||
const auto pointer =
|
||||
DescriptorElementPointer(state, descriptors.pointer_type, descriptors.variable, array_index,
|
||||
kind, resource, "storage image descriptor array was not emitted");
|
||||
const auto pointer = DescriptorElementPointerId(
|
||||
state, descriptors.pointer_type, descriptors.variable, array_index_id, kind, resource,
|
||||
"storage image descriptor array was not emitted");
|
||||
const auto image = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, descriptors.image_type, image, pointer});
|
||||
return image;
|
||||
@@ -168,12 +168,38 @@ void EmitImageStore(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto view = StorageImageViewKind(state, inst.memory, uint_image, inst.pc);
|
||||
const auto binding =
|
||||
ResourceForDescriptor(state, StorageBindingKind(uint_image, view), inst.memory.resource);
|
||||
const auto image = LoadStorageImageDescriptorAtIndex(state, inst.memory.resource,
|
||||
binding.array_index, uint_image, view);
|
||||
const auto emit_write = [&](uint32_t descriptor_index, bool non_uniform) {
|
||||
if (non_uniform) {
|
||||
state.builder.AddAnnotation({OpDecorate, descriptor_index, DecorationNonUniform});
|
||||
}
|
||||
const auto image = LoadStorageImageDescriptorAtIndex(state, inst.memory.resource,
|
||||
descriptor_index, uint_image, view);
|
||||
if (non_uniform) {
|
||||
state.builder.AddAnnotation({OpDecorate, image, DecorationNonUniform});
|
||||
}
|
||||
state.builder.AddFunction({OpImageWrite, image, EmitImageCoordU32(state, inst, view),
|
||||
uint_image ? EmitImageStoreTexelU32(state, inst)
|
||||
: EmitImageStoreTexelF32(state, inst)});
|
||||
};
|
||||
if (!inst.memory.image_has_mip) {
|
||||
emit_write(ConstantU32(state, binding.array_index), false);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& resource = state.program.info.images[inst.memory.resource];
|
||||
const auto mip = EmitImageMipLodU32(state, inst, inst.src[1], view);
|
||||
const auto in_range = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpImageWrite, image, EmitImageCoordU32(state, inst, view),
|
||||
uint_image ? EmitImageStoreTexelU32(state, inst) : EmitImageStoreTexelF32(state, inst)});
|
||||
{OpULessThan, state.bool_type, in_range, mip, ConstantU32(state, resource.mip_levels)});
|
||||
EmitIfCondition(state, in_range, [&] {
|
||||
auto descriptor_index = mip;
|
||||
if (binding.array_index != 0) {
|
||||
descriptor_index = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpIAdd, state.uint_type, descriptor_index,
|
||||
ConstantU32(state, binding.array_index), mip});
|
||||
}
|
||||
emit_write(descriptor_index, true);
|
||||
});
|
||||
}
|
||||
|
||||
void EmitImageSampleResult(EmitterState& state, const IR::Instruction& inst, uint32_t sample,
|
||||
|
||||
@@ -23,38 +23,40 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
enum : uint32_t {
|
||||
ExecutionModelVertex = 0,
|
||||
ExecutionModelFragment = 4,
|
||||
ExecutionModelGLCompute = 5,
|
||||
ExecutionModeOriginUpperLeft = 7,
|
||||
ExecutionModeEarlyFragmentTests = 9,
|
||||
ExecutionModeDepthReplacing = 12,
|
||||
ExecutionModeLocalSize = 17,
|
||||
ExecutionModeDerivativeGroupQuadsKHR = 5289,
|
||||
AddressingModelLogical = 0,
|
||||
MemoryModelGLSL450 = 1,
|
||||
CapabilityShader = 1,
|
||||
CapabilityImageGatherExtended = 25,
|
||||
CapabilitySampled1D = 43,
|
||||
CapabilityImage1D = 44,
|
||||
CapabilityImageQuery = 50,
|
||||
CapabilityStorageImageReadWithoutFormat = 55,
|
||||
CapabilityStorageImageWriteWithoutFormat = 56,
|
||||
CapabilityGroupNonUniform = 61,
|
||||
CapabilityGroupNonUniformBallot = 64,
|
||||
CapabilityGroupNonUniformShuffle = 65,
|
||||
CapabilityComputeDerivativeGroupQuadsKHR = 5288,
|
||||
StorageClassUniformConstant = 0,
|
||||
StorageClassInput = 1,
|
||||
StorageClassOutput = 3,
|
||||
StorageClassWorkgroup = 4,
|
||||
StorageClassFunction = 7,
|
||||
StorageClassPushConstant = 9,
|
||||
StorageClassImage = 11,
|
||||
StorageClassStorageBuffer = 12,
|
||||
FunctionControlNone = 0,
|
||||
SelectionControlNone = 0,
|
||||
LoopControlNone = 0,
|
||||
ExecutionModelVertex = 0,
|
||||
ExecutionModelFragment = 4,
|
||||
ExecutionModelGLCompute = 5,
|
||||
ExecutionModeOriginUpperLeft = 7,
|
||||
ExecutionModeEarlyFragmentTests = 9,
|
||||
ExecutionModeDepthReplacing = 12,
|
||||
ExecutionModeLocalSize = 17,
|
||||
ExecutionModeDerivativeGroupQuadsKHR = 5289,
|
||||
AddressingModelLogical = 0,
|
||||
MemoryModelGLSL450 = 1,
|
||||
CapabilityShader = 1,
|
||||
CapabilityImageGatherExtended = 25,
|
||||
CapabilitySampled1D = 43,
|
||||
CapabilityImage1D = 44,
|
||||
CapabilityImageQuery = 50,
|
||||
CapabilityStorageImageReadWithoutFormat = 55,
|
||||
CapabilityStorageImageWriteWithoutFormat = 56,
|
||||
CapabilityGroupNonUniform = 61,
|
||||
CapabilityGroupNonUniformBallot = 64,
|
||||
CapabilityGroupNonUniformShuffle = 65,
|
||||
CapabilityShaderNonUniform = 5301,
|
||||
CapabilityStorageImageArrayNonUniformIndexing = 5309,
|
||||
CapabilityComputeDerivativeGroupQuadsKHR = 5288,
|
||||
StorageClassUniformConstant = 0,
|
||||
StorageClassInput = 1,
|
||||
StorageClassOutput = 3,
|
||||
StorageClassWorkgroup = 4,
|
||||
StorageClassFunction = 7,
|
||||
StorageClassPushConstant = 9,
|
||||
StorageClassImage = 11,
|
||||
StorageClassStorageBuffer = 12,
|
||||
FunctionControlNone = 0,
|
||||
SelectionControlNone = 0,
|
||||
LoopControlNone = 0,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
@@ -67,6 +69,7 @@ enum : uint32_t {
|
||||
DecorationBinding = 33,
|
||||
DecorationDescriptorSet = 34,
|
||||
DecorationOffset = 35,
|
||||
DecorationNonUniform = 5300,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
@@ -679,6 +682,10 @@ uint32_t DescriptorElementPointer(EmitterState& state, uint32_t result_ptr_type,
|
||||
uint32_t variable_id, uint32_t array_index,
|
||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
||||
const char* variable_name);
|
||||
uint32_t DescriptorElementPointerId(EmitterState& state, uint32_t result_ptr_type,
|
||||
uint32_t variable_id, uint32_t array_index_id,
|
||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
||||
const char* variable_name);
|
||||
|
||||
ImageViewKind SampledImageViewKind(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc);
|
||||
|
||||
@@ -484,6 +484,13 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
if (state.needs_image_gather_extended) {
|
||||
state.builder.AddCapability({CapabilityImageGatherExtended});
|
||||
}
|
||||
if (std::any_of(
|
||||
state.program.info.images.begin(), state.program.info.images.end(),
|
||||
[](const auto& image) { return image.mip_mode == IR::ImageMipMode::DynamicStorage; })) {
|
||||
state.builder.AddCapability({CapabilityShaderNonUniform});
|
||||
state.builder.AddCapability({CapabilityStorageImageArrayNonUniformIndexing});
|
||||
state.builder.AddExtension("SPV_EXT_descriptor_indexing");
|
||||
}
|
||||
if (std::any_of(state.storage_images.begin(),
|
||||
state.storage_images.begin() + StorageImageViewKindCount,
|
||||
[](const auto& image) { return image.variable != 0; })) {
|
||||
|
||||
@@ -269,7 +269,10 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
||||
}
|
||||
return false;
|
||||
}
|
||||
image_groups[static_cast<size_t>(group - ImageBindingKinds.begin())].push_back(i);
|
||||
auto& resources = image_groups[static_cast<size_t>(group - ImageBindingKinds.begin())];
|
||||
for (uint32_t binding = 0; binding < program.info.images[i].NumBindings(); binding++) {
|
||||
resources.push_back(i);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < image_groups.size(); i++) {
|
||||
if (!image_groups[i].empty()) {
|
||||
|
||||
@@ -71,6 +71,17 @@ bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||
Prospero::ImageType::kCube;
|
||||
}
|
||||
|
||||
bool DescriptorMipRange(const DescriptorValue& descriptor, uint32_t& count) {
|
||||
const auto base_level = (descriptor.dwords[3] >> 12u) & 0xfu;
|
||||
const auto last_level = (descriptor.dwords[3] >> 16u) & 0xfu;
|
||||
const auto max_mip = (descriptor.dwords[5] >> 4u) & 0xfu;
|
||||
if (base_level > last_level || last_level > max_mip) {
|
||||
return false;
|
||||
}
|
||||
count = last_level - base_level + 1u;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeBufferDescriptor(const DescriptorValue& descriptor, ShaderBufferResource& result) {
|
||||
if (descriptor.dword_count != std::size(result.fields)) {
|
||||
return false;
|
||||
@@ -196,16 +207,31 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (image.mip_mode == ImageMipMode::DynamicStorage) {
|
||||
uint32_t mip_levels = 0;
|
||||
if (!DescriptorMipRange(descriptor, mip_levels) || mip_levels != image.mip_levels) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches specialized storage mip count", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (image.mip_levels != 1u) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("image descriptor {} has invalid non-storage mip count", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension ||
|
||||
DescriptorIsCube(descriptor) != image.cube) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches specialized dimension: "
|
||||
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||
descriptor.dwords[6], descriptor.dwords[7]);
|
||||
*error =
|
||||
fmt::format("image descriptor {} no longer matches specialized dimension: "
|
||||
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||
descriptor.dwords[6], descriptor.dwords[7]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -224,10 +250,9 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const bool raw_sint_storage =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
|
||||
!image.read && !image.atomic;
|
||||
const bool uint_descriptor =
|
||||
Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
@@ -380,8 +405,9 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
auto& image = next.images[i];
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
image.cube = false;
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
image.cube = false;
|
||||
image.mip_levels = 1;
|
||||
switch (image.kind) {
|
||||
case ResourceKind::ImageUint: image.kind = ResourceKind::Image; break;
|
||||
case ResourceKind::StorageImageUint:
|
||||
@@ -393,6 +419,16 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (image.mip_mode == ImageMipMode::DynamicStorage) {
|
||||
if (!DescriptorMipRange(descriptor, image.mip_levels)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("image descriptor {} has invalid storage mip range", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
image.mip_levels = 1;
|
||||
}
|
||||
const auto descriptor_dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (descriptor_dimension == Decoder::ImageDimension::Unknown) {
|
||||
if (error != nullptr) {
|
||||
|
||||
@@ -598,11 +598,14 @@ enum class ImageMipMode { None, DynamicStorage };
|
||||
constexpr uint32_t StorageImageIdentitySwizzle = 0x00000facu;
|
||||
|
||||
struct ImageResource {
|
||||
static constexpr uint32_t MaxMipLevels = 16;
|
||||
|
||||
uint32_t source = 0;
|
||||
uint32_t first_use_pc = 0;
|
||||
ResourceKind kind = ResourceKind::None;
|
||||
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
|
||||
ImageMipMode mip_mode = ImageMipMode::None;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t storage_swizzle = StorageImageIdentitySwizzle;
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
@@ -610,6 +613,10 @@ struct ImageResource {
|
||||
bool depth_compare = false;
|
||||
bool cube = false;
|
||||
|
||||
[[nodiscard]] uint32_t NumBindings() const {
|
||||
return mip_mode == ImageMipMode::DynamicStorage ? mip_levels : 1u;
|
||||
}
|
||||
|
||||
bool operator==(const ImageResource& other) const = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -1018,6 +1018,7 @@ static void ShaderAppendNativeSpecialization(std::vector<uint32_t>&
|
||||
ids.push_back(static_cast<uint32_t>(image.kind));
|
||||
ids.push_back(static_cast<uint32_t>(image.dimension));
|
||||
ids.push_back(static_cast<uint32_t>(image.mip_mode));
|
||||
ids.push_back(image.mip_levels);
|
||||
ids.push_back(image.storage_swizzle);
|
||||
}
|
||||
ids.push_back(static_cast<uint32_t>(program.info.addresses.size()));
|
||||
|
||||
Reference in New Issue
Block a user