mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
graphics: support dynamic storage mip writes
This commit is contained in:
@@ -55,6 +55,7 @@ struct ImageBinding {
|
||||
bool is_target = false;
|
||||
bool needs_rebind = false;
|
||||
bool force_general = false;
|
||||
bool shader_write = false;
|
||||
};
|
||||
|
||||
class Image final {
|
||||
|
||||
@@ -120,7 +120,11 @@ 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 &&
|
||||
((resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
|
||||
resource.mip_count == 1u) ||
|
||||
(resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage &&
|
||||
resource.mip_count != 0u)) &&
|
||||
resource.written &&
|
||||
(!resource.atomic ||
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.read)) &&
|
||||
|
||||
@@ -94,10 +94,14 @@ 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 element) {
|
||||
const auto view =
|
||||
texture.mip_views.empty()
|
||||
? (element == 0u ? texture.image_view : vk::ImageView {})
|
||||
: (element < texture.mip_views.size() ? texture.mip_views[element] : vk::ImageView {});
|
||||
EXIT_IF(!texture.image_id || view == nullptr || texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, view, texture.layout};
|
||||
}
|
||||
|
||||
DescriptorCache::~DescriptorCache() {
|
||||
@@ -229,14 +233,17 @@ 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;
|
||||
size_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;
|
||||
buffer_infos.reserve(descriptor_count);
|
||||
image_infos.reserve(descriptor_count);
|
||||
writes.reserve(program.bindings.descriptors.size());
|
||||
std::vector<uint32_t> image_occurrences(data.images.size());
|
||||
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
vk::WriteDescriptorSet write {};
|
||||
@@ -273,7 +280,7 @@ VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage
|
||||
default: {
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& texture = data.images.at(resource);
|
||||
image_infos.push_back(MakeImageInfo(texture));
|
||||
image_infos.push_back(MakeImageInfo(texture, image_occurrences.at(resource)++));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -286,6 +293,12 @@ VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage
|
||||
}
|
||||
writes.push_back(write);
|
||||
}
|
||||
for (uint32_t i = 0; i < data.images.size(); i++) {
|
||||
const auto expected = data.images[i].mip_views.empty()
|
||||
? 1u
|
||||
: static_cast<uint32_t>(data.images[i].mip_views.size());
|
||||
EXIT_IF(image_occurrences[i] != expected);
|
||||
}
|
||||
m_graphics.device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
|
||||
nullptr);
|
||||
return *set;
|
||||
|
||||
@@ -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,8 @@ private:
|
||||
int next_free_pool = -1;
|
||||
};
|
||||
|
||||
static vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture);
|
||||
static vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture,
|
||||
uint32_t element = 0);
|
||||
void CreatePool();
|
||||
VulkanDescriptorSet* Allocate(Stage stage, const ShaderRecompiler::IR::Program& program);
|
||||
vk::DescriptorSetLayout
|
||||
|
||||
@@ -786,9 +786,10 @@ void RenderExecutor::BindImage(ImageId id, bool storage) {
|
||||
return;
|
||||
}
|
||||
if (image.binding.is_bound) {
|
||||
image.binding.force_general |= storage;
|
||||
image.binding.force_general |= image.binding.shader_write != storage;
|
||||
}
|
||||
image.binding.is_bound = true;
|
||||
image.binding.shader_write |= storage;
|
||||
TrackImageBinding(id);
|
||||
}
|
||||
|
||||
@@ -904,8 +905,27 @@ void RenderExecutor::RebindImages(CommandBuffer& buffer,
|
||||
BindImage(images[i].image_id,
|
||||
images[i].desc.type == TextureCache::BindingType::Storage);
|
||||
}
|
||||
auto& binding = images[i];
|
||||
binding.image_view = texture_cache.FindTexture(binding.image_id, binding.desc);
|
||||
auto& binding = images[i];
|
||||
binding.mip_views.clear();
|
||||
const auto& resource = program.info.images[i];
|
||||
if (resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage) {
|
||||
EXIT_IF(resource.mip_count == 0u ||
|
||||
resource.mip_count != binding.desc.view_info.level_count);
|
||||
binding.mip_views.reserve(resource.mip_count);
|
||||
for (uint32_t mip = 0; mip < resource.mip_count; mip++) {
|
||||
auto desc = binding.desc;
|
||||
desc.view_info.base_level += mip;
|
||||
desc.view_info.level_count = 1;
|
||||
binding.mip_views.push_back(texture_cache.FindTexture(binding.image_id, desc));
|
||||
}
|
||||
binding.image_view = binding.mip_views.front();
|
||||
} else {
|
||||
auto desc = binding.desc;
|
||||
if (desc.type == TextureCache::BindingType::Storage) {
|
||||
desc.view_info.level_count = 1;
|
||||
}
|
||||
binding.image_view = texture_cache.FindTexture(binding.image_id, desc);
|
||||
}
|
||||
auto& image = texture_cache.GetImage(binding.image_id);
|
||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||
image.usage.storage |= storage;
|
||||
@@ -994,8 +1014,11 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
range, vk_buffer);
|
||||
} else if ((image.binding.force_general || image.binding.is_target) &&
|
||||
!image.info.IsDepth()) {
|
||||
const vk::AccessFlags2 storage_access = image.binding.shader_write
|
||||
? vk::AccessFlagBits2::eShaderWrite
|
||||
: vk::AccessFlags2 {};
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead |
|
||||
vk::AccessFlagBits2::eShaderRead | storage_access |
|
||||
vk::AccessFlagBits2::eColorAttachmentRead |
|
||||
vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||
{}, vk_buffer);
|
||||
|
||||
@@ -90,7 +90,13 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
return Fail(error, "native shader plan has an invalid image class");
|
||||
}
|
||||
present[static_cast<size_t>(kind)] = true;
|
||||
expected[static_cast<size_t>(kind)].push_back(i);
|
||||
const auto dynamic = program.info.images[i].mip_mode == IR::ImageMipMode::DynamicStorage;
|
||||
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
||||
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
||||
return Fail(error, "native shader plan has an invalid image mip descriptor count");
|
||||
}
|
||||
expected[static_cast<size_t>(kind)].insert(expected[static_cast<size_t>(kind)].end(), count,
|
||||
i);
|
||||
}
|
||||
if (!program.info.samplers.empty()) {
|
||||
Expect(Kind::Samplers, Dense(program.info.samplers.size()));
|
||||
|
||||
@@ -469,6 +469,48 @@ uint32_t LoadStorageImageDescriptor(EmitterState& state, uint32_t resource, bool
|
||||
return image;
|
||||
}
|
||||
|
||||
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, bool uint_image,
|
||||
ImageViewKind view, uint32_t mip_lod, uint32_t coord, uint32_t texel) {
|
||||
const auto kind = StorageBindingKind(uint_image, view);
|
||||
const auto binding = ResourceForDescriptor(state, kind, resource);
|
||||
const auto& image = state.program.info.images.at(resource);
|
||||
const auto LoadAt = [&](uint32_t array_index) {
|
||||
const auto pointer = DescriptorElementPointer(
|
||||
state, StorageImagePointerType(state, uint_image, view),
|
||||
StorageImageVariable(state, uint_image, view), array_index, kind, resource,
|
||||
"storage image descriptor array was not emitted");
|
||||
const auto descriptor = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpLoad, StorageImageType(state, uint_image, view), descriptor, pointer});
|
||||
return descriptor;
|
||||
};
|
||||
if (image.mip_mode != IR::ImageMipMode::DynamicStorage) {
|
||||
state.builder.AddFunction({OpImageWrite, LoadAt(binding.array_index), coord, texel});
|
||||
return;
|
||||
}
|
||||
if (image.mip_count == 0u) {
|
||||
ExitDescriptorBindingFailure(state, kind, resource,
|
||||
"dynamic storage image has no mip descriptors");
|
||||
}
|
||||
|
||||
const auto merge_label = state.builder.AllocateId();
|
||||
std::vector<uint32_t> labels(image.mip_count);
|
||||
std::vector<uint32_t> words {OpSwitch, mip_lod, merge_label};
|
||||
for (uint32_t mip = 0; mip < image.mip_count; mip++) {
|
||||
labels[mip] = state.builder.AllocateId();
|
||||
words.push_back(mip);
|
||||
words.push_back(labels[mip]);
|
||||
}
|
||||
state.builder.AddFunction({OpSelectionMerge, merge_label, SelectionControlNone});
|
||||
state.builder.AddFunction(words);
|
||||
for (uint32_t mip = 0; mip < image.mip_count; mip++) {
|
||||
state.builder.AddFunction({OpLabel, labels[mip]});
|
||||
state.builder.AddFunction({OpImageWrite, LoadAt(binding.array_index + mip), coord, texel});
|
||||
state.builder.AddFunction({OpBranch, merge_label});
|
||||
}
|
||||
state.builder.AddFunction({OpLabel, merge_label});
|
||||
}
|
||||
|
||||
uint32_t ExecutionModelForStage(ShaderType stage) {
|
||||
switch (stage) {
|
||||
case ShaderType::Vertex: return ExecutionModelVertex;
|
||||
|
||||
@@ -731,6 +731,9 @@ uint32_t StorageImageDescriptorPointer(EmitterState& state, uint32_t resource, b
|
||||
uint32_t LoadStorageImageDescriptor(EmitterState& state, uint32_t resource, bool uint_image,
|
||||
uint32_t use_pc, ImageViewKind view = ImageViewKind::Dim2D);
|
||||
|
||||
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, bool uint_image,
|
||||
ImageViewKind view, uint32_t mip_lod, uint32_t coord, uint32_t texel);
|
||||
|
||||
uint32_t ExecutionModelForStage(ShaderType stage);
|
||||
|
||||
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||
|
||||
@@ -408,11 +408,13 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto uint_image = mem.kind == IR::ResourceKind::StorageImageUint;
|
||||
const auto view = StorageImageViewKind(state, mem, uint_image, pc);
|
||||
EmitIfCondition(state, ctx.Arg(inst, 3), [&]() {
|
||||
state.builder.AddFunction(
|
||||
{OpImageWrite,
|
||||
LoadStorageImageDescriptor(state, mem.resource, uint_image, pc, view),
|
||||
CoordU32(ctx, mem, *address, view),
|
||||
StoreTexel(ctx, mem, ctx.Arg(inst, 2), uint_image)});
|
||||
const auto mip_lod =
|
||||
state.program.info.images[mem.resource].mip_mode == IR::ImageMipMode::DynamicStorage
|
||||
? LodU32(ctx, mem, *address, view)
|
||||
: 0u;
|
||||
const auto coord = CoordU32(ctx, mem, *address, view);
|
||||
const auto texel = StoreTexel(ctx, mem, ctx.Arg(inst, 2), uint_image);
|
||||
EmitStorageImageWrite(state, mem.resource, uint_image, view, mip_lod, coord, texel);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -194,6 +194,7 @@ struct ImageResource {
|
||||
ResourceKind kind = ResourceKind::None;
|
||||
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
|
||||
ImageMipMode mip_mode = ImageMipMode::None;
|
||||
uint32_t mip_count = 1;
|
||||
uint32_t storage_swizzle = StorageImageIdentitySwizzle;
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
|
||||
@@ -196,7 +196,16 @@ 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())];
|
||||
const auto dynamic = program.info.images[i].mip_mode == ImageMipMode::DynamicStorage;
|
||||
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
||||
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
||||
if (error != nullptr) {
|
||||
*error = "image has an invalid specialized mip descriptor count";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
resources.insert(resources.end(), count, i);
|
||||
}
|
||||
for (uint32_t i = 0; i < image_groups.size(); i++) {
|
||||
if (!image_groups[i].empty()) {
|
||||
|
||||
@@ -72,6 +72,15 @@ bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||
Prospero::ImageType::kCube;
|
||||
}
|
||||
|
||||
uint32_t StorageMipCount(const ImageResource& image, const DescriptorValue& descriptor) {
|
||||
if (image.mip_mode != ImageMipMode::DynamicStorage || NullImageDescriptor(descriptor)) {
|
||||
return 1;
|
||||
}
|
||||
const auto base = (descriptor.dwords[3] >> 12u) & 0xfu;
|
||||
const auto last = (descriptor.dwords[3] >> 16u) & 0xfu;
|
||||
return base <= last ? last - base + 1u : 0u;
|
||||
}
|
||||
|
||||
bool DecodeBufferDescriptor(const DescriptorValue& descriptor, ShaderBufferResource& result) {
|
||||
if (descriptor.dword_count != std::size(result.fields)) {
|
||||
return false;
|
||||
@@ -201,6 +210,13 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto& image = program.info.images[i];
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
const auto mip_count = StorageMipCount(image, descriptor);
|
||||
if (mip_count == 0u || mip_count != image.mip_count) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("storage image descriptor {} changed mip range", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
bool canonical_kind =
|
||||
image.kind == ResourceKind::Image || image.kind == ResourceKind::StorageImage;
|
||||
@@ -404,6 +420,13 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
for (uint32_t i = 0; i < next.images.size(); i++) {
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
auto& image = next.images[i];
|
||||
image.mip_count = StorageMipCount(image, descriptor);
|
||||
if (image.mip_count == 0u) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("storage image descriptor {} has an invalid mip range", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
image.cube = false;
|
||||
|
||||
@@ -1022,9 +1022,10 @@ static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Prog
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto& image = program.info.images[i];
|
||||
ret += fmt::format(" i{}[kind={} dim={} swizzle=0x{:03x}]", i,
|
||||
static_cast<uint32_t>(image.kind),
|
||||
static_cast<uint32_t>(image.dimension), image.storage_swizzle);
|
||||
ret += fmt::format(
|
||||
" i{}[kind={} dim={} mip={}/{} swizzle=0x{:03x}]", i, static_cast<uint32_t>(image.kind),
|
||||
static_cast<uint32_t>(image.dimension), static_cast<uint32_t>(image.mip_mode),
|
||||
image.mip_count, image.storage_swizzle);
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||
ret += fmt::format(" a{}[base=0x{:x}]", i, program.info.addresses[i].specialized_base);
|
||||
@@ -1062,6 +1063,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_count);
|
||||
ids.push_back(image.storage_swizzle);
|
||||
}
|
||||
ids.push_back(static_cast<uint32_t>(program.info.addresses.size()));
|
||||
|
||||
Reference in New Issue
Block a user