diff options
Diffstat (limited to 'src/shader_recompiler')
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_context.cpp | 29 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_context.h | 5 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv.cpp | 1 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | 24 | ||||
| -rw-r--r-- | src/shader_recompiler/ir_opt/texture_pass.cpp | 80 | ||||
| -rw-r--r-- | src/shader_recompiler/shader_info.h | 9 |
6 files changed, 125 insertions, 23 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 2d39ea373..d01633628 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -46,6 +46,8 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | |||
| 46 | return ctx.TypeImage(type, spv::Dim::Cube, true, false, false, 1, format); | 46 | return ctx.TypeImage(type, spv::Dim::Cube, true, false, false, 1, format); |
| 47 | case TextureType::ShadowArrayCube: | 47 | case TextureType::ShadowArrayCube: |
| 48 | return ctx.TypeImage(type, spv::Dim::Cube, true, true, false, 1, format); | 48 | return ctx.TypeImage(type, spv::Dim::Cube, true, true, false, 1, format); |
| 49 | case TextureType::Buffer: | ||
| 50 | break; | ||
| 49 | } | 51 | } |
| 50 | throw InvalidArgument("Invalid texture type {}", desc.type); | 52 | throw InvalidArgument("Invalid texture type {}", desc.type); |
| 51 | } | 53 | } |
| @@ -129,6 +131,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin | |||
| 129 | DefineConstantBuffers(program.info, binding); | 131 | DefineConstantBuffers(program.info, binding); |
| 130 | DefineStorageBuffers(program.info, binding); | 132 | DefineStorageBuffers(program.info, binding); |
| 131 | DefineTextures(program.info, binding); | 133 | DefineTextures(program.info, binding); |
| 134 | DefineTextureBuffers(program.info, binding); | ||
| 132 | DefineAttributeMemAccess(program.info); | 135 | DefineAttributeMemAccess(program.info); |
| 133 | DefineLabels(program); | 136 | DefineLabels(program); |
| 134 | } | 137 | } |
| @@ -541,6 +544,32 @@ void EmitContext::DefineTextures(const Info& info, u32& binding) { | |||
| 541 | } | 544 | } |
| 542 | } | 545 | } |
| 543 | 546 | ||
| 547 | void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) { | ||
| 548 | if (info.texture_buffer_descriptors.empty()) { | ||
| 549 | return; | ||
| 550 | } | ||
| 551 | const spv::ImageFormat format{spv::ImageFormat::Unknown}; | ||
| 552 | image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format); | ||
| 553 | sampled_texture_buffer_type = TypeSampledImage(image_buffer_type); | ||
| 554 | |||
| 555 | const Id type{TypePointer(spv::StorageClass::UniformConstant, sampled_texture_buffer_type)}; | ||
| 556 | texture_buffers.reserve(info.texture_buffer_descriptors.size()); | ||
| 557 | for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) { | ||
| 558 | if (desc.count != 1) { | ||
| 559 | throw NotImplementedException("Array of texture buffers"); | ||
| 560 | } | ||
| 561 | const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)}; | ||
| 562 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 563 | Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 564 | Name(id, fmt::format("texbuf{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); | ||
| 565 | texture_buffers.insert(texture_buffers.end(), desc.count, id); | ||
| 566 | if (profile.supported_spirv >= 0x00010400) { | ||
| 567 | interfaces.push_back(id); | ||
| 568 | } | ||
| 569 | binding += desc.count; | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 544 | void EmitContext::DefineLabels(IR::Program& program) { | 573 | void EmitContext::DefineLabels(IR::Program& program) { |
| 545 | for (IR::Block* const block : program.blocks) { | 574 | for (IR::Block* const block : program.blocks) { |
| 546 | block->SetDefinition(OpLabel()); | 575 | block->SetDefinition(OpLabel()); |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index 7a2ac0511..2a10e94e5 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -90,9 +90,13 @@ public: | |||
| 90 | 90 | ||
| 91 | Id storage_u32{}; | 91 | Id storage_u32{}; |
| 92 | 92 | ||
| 93 | Id image_buffer_type{}; | ||
| 94 | Id sampled_texture_buffer_type{}; | ||
| 95 | |||
| 93 | std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; | 96 | std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; |
| 94 | std::array<Id, Info::MAX_SSBOS> ssbos{}; | 97 | std::array<Id, Info::MAX_SSBOS> ssbos{}; |
| 95 | std::vector<TextureDefinition> textures; | 98 | std::vector<TextureDefinition> textures; |
| 99 | std::vector<Id> texture_buffers; | ||
| 96 | 100 | ||
| 97 | Id workgroup_id{}; | 101 | Id workgroup_id{}; |
| 98 | Id local_invocation_id{}; | 102 | Id local_invocation_id{}; |
| @@ -151,6 +155,7 @@ private: | |||
| 151 | void DefineConstantBuffers(const Info& info, u32& binding); | 155 | void DefineConstantBuffers(const Info& info, u32& binding); |
| 152 | void DefineStorageBuffers(const Info& info, u32& binding); | 156 | void DefineStorageBuffers(const Info& info, u32& binding); |
| 153 | void DefineTextures(const Info& info, u32& binding); | 157 | void DefineTextures(const Info& info, u32& binding); |
| 158 | void DefineTextureBuffers(const Info& info, u32& binding); | ||
| 154 | void DefineAttributeMemAccess(const Info& info); | 159 | void DefineAttributeMemAccess(const Info& info); |
| 155 | void DefineLabels(IR::Program& program); | 160 | void DefineLabels(IR::Program& program); |
| 156 | 161 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 191380db0..32512a0e5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -249,6 +249,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | |||
| 249 | // TODO: Track this usage | 249 | // TODO: Track this usage |
| 250 | ctx.AddCapability(spv::Capability::ImageGatherExtended); | 250 | ctx.AddCapability(spv::Capability::ImageGatherExtended); |
| 251 | ctx.AddCapability(spv::Capability::ImageQuery); | 251 | ctx.AddCapability(spv::Capability::ImageQuery); |
| 252 | ctx.AddCapability(spv::Capability::SampledBuffer); | ||
| 252 | } | 253 | } |
| 253 | 254 | ||
| 254 | Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { | 255 | Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index fc40615af..525f67c6e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -128,12 +128,18 @@ Id Texture(EmitContext& ctx, const IR::Value& index) { | |||
| 128 | throw NotImplementedException("Indirect texture sample"); | 128 | throw NotImplementedException("Indirect texture sample"); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | Id TextureImage(EmitContext& ctx, const IR::Value& index) { | 131 | Id TextureImage(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { |
| 132 | if (index.IsImmediate()) { | 132 | if (!index.IsImmediate()) { |
| 133 | throw NotImplementedException("Indirect texture sample"); | ||
| 134 | } | ||
| 135 | if (info.type == TextureType::Buffer) { | ||
| 136 | const Id sampler_id{ctx.texture_buffers.at(index.U32())}; | ||
| 137 | const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)}; | ||
| 138 | return ctx.OpImage(ctx.image_buffer_type, id); | ||
| 139 | } else { | ||
| 133 | const TextureDefinition def{ctx.textures.at(index.U32())}; | 140 | const TextureDefinition def{ctx.textures.at(index.U32())}; |
| 134 | return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); | 141 | return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); |
| 135 | } | 142 | } |
| 136 | throw NotImplementedException("Indirect texture sample"); | ||
| 137 | } | 143 | } |
| 138 | 144 | ||
| 139 | Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { | 145 | Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { |
| @@ -297,17 +303,22 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | |||
| 297 | ctx.F32[4], Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); | 303 | ctx.F32[4], Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); |
| 298 | } | 304 | } |
| 299 | 305 | ||
| 306 | #pragma optimize("", off) | ||
| 307 | |||
| 300 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | 308 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, |
| 301 | Id lod, Id ms) { | 309 | Id lod, Id ms) { |
| 302 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 310 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 311 | if (info.type == TextureType::Buffer) { | ||
| 312 | lod = Id{}; | ||
| 313 | } | ||
| 303 | const ImageOperands operands(offset, lod, ms); | 314 | const ImageOperands operands(offset, lod, ms); |
| 304 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], | 315 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], |
| 305 | TextureImage(ctx, index), coords, operands.Mask(), operands.Span()); | 316 | TextureImage(ctx, index, info), coords, operands.Mask(), operands.Span()); |
| 306 | } | 317 | } |
| 307 | 318 | ||
| 308 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { | 319 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { |
| 309 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 320 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 310 | const Id image{TextureImage(ctx, index)}; | 321 | const Id image{TextureImage(ctx, index, info)}; |
| 311 | const Id zero{ctx.u32_zero_value}; | 322 | const Id zero{ctx.u32_zero_value}; |
| 312 | const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; | 323 | const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; |
| 313 | switch (info.type) { | 324 | switch (info.type) { |
| @@ -331,6 +342,9 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i | |||
| 331 | case TextureType::ShadowArrayCube: | 342 | case TextureType::ShadowArrayCube: |
| 332 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), | 343 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), |
| 333 | mips()); | 344 | mips()); |
| 345 | case TextureType::Buffer: | ||
| 346 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero, | ||
| 347 | zero, mips()); | ||
| 334 | } | 348 | } |
| 335 | throw LogicError("Unspecified image type {}", info.type.Value()); | 349 | throw LogicError("Unspecified image type {}", info.type.Value()); |
| 336 | } | 350 | } |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index bcb94ce4d..290ce4179 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -147,24 +147,39 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { | |||
| 147 | 147 | ||
| 148 | class Descriptors { | 148 | class Descriptors { |
| 149 | public: | 149 | public: |
| 150 | explicit Descriptors(TextureDescriptors& descriptors_) : descriptors{descriptors_} {} | 150 | explicit Descriptors(TextureDescriptors& texture_descriptors_, |
| 151 | TextureBufferDescriptors& texture_buffer_descriptors_) | ||
| 152 | : texture_descriptors{texture_descriptors_}, texture_buffer_descriptors{ | ||
| 153 | texture_buffer_descriptors_} {} | ||
| 154 | |||
| 155 | u32 Add(const TextureDescriptor& desc) { | ||
| 156 | return Add(texture_descriptors, desc, [&desc](const auto& existing) { | ||
| 157 | return desc.cbuf_index == existing.cbuf_index && | ||
| 158 | desc.cbuf_offset == existing.cbuf_offset && desc.type == existing.type; | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | |||
| 162 | u32 Add(const TextureBufferDescriptor& desc) { | ||
| 163 | return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { | ||
| 164 | return desc.cbuf_index == existing.cbuf_index && | ||
| 165 | desc.cbuf_offset == existing.cbuf_offset; | ||
| 166 | }); | ||
| 167 | } | ||
| 151 | 168 | ||
| 152 | u32 Add(const TextureDescriptor& descriptor) { | 169 | private: |
| 170 | template <typename Descriptors, typename Descriptor, typename Func> | ||
| 171 | static u32 Add(Descriptors& descriptors, const Descriptor& desc, Func&& pred) { | ||
| 153 | // TODO: Handle arrays | 172 | // TODO: Handle arrays |
| 154 | auto it{std::ranges::find_if(descriptors, [&descriptor](const TextureDescriptor& existing) { | 173 | const auto it{std::ranges::find_if(descriptors, pred)}; |
| 155 | return descriptor.cbuf_index == existing.cbuf_index && | ||
| 156 | descriptor.cbuf_offset == existing.cbuf_offset && | ||
| 157 | descriptor.type == existing.type; | ||
| 158 | })}; | ||
| 159 | if (it != descriptors.end()) { | 174 | if (it != descriptors.end()) { |
| 160 | return static_cast<u32>(std::distance(descriptors.begin(), it)); | 175 | return static_cast<u32>(std::distance(descriptors.begin(), it)); |
| 161 | } | 176 | } |
| 162 | descriptors.push_back(descriptor); | 177 | descriptors.push_back(desc); |
| 163 | return static_cast<u32>(descriptors.size()) - 1; | 178 | return static_cast<u32>(descriptors.size()) - 1; |
| 164 | } | 179 | } |
| 165 | 180 | ||
| 166 | private: | 181 | TextureDescriptors& texture_descriptors; |
| 167 | TextureDescriptors& descriptors; | 182 | TextureBufferDescriptors& texture_buffer_descriptors; |
| 168 | }; | 183 | }; |
| 169 | } // Anonymous namespace | 184 | } // Anonymous namespace |
| 170 | 185 | ||
| @@ -185,7 +200,10 @@ void TexturePass(Environment& env, IR::Program& program) { | |||
| 185 | std::stable_sort(to_replace.begin(), to_replace.end(), [](const auto& lhs, const auto& rhs) { | 200 | std::stable_sort(to_replace.begin(), to_replace.end(), [](const auto& lhs, const auto& rhs) { |
| 186 | return lhs.cbuf.index < rhs.cbuf.index; | 201 | return lhs.cbuf.index < rhs.cbuf.index; |
| 187 | }); | 202 | }); |
| 188 | Descriptors descriptors{program.info.texture_descriptors}; | 203 | Descriptors descriptors{ |
| 204 | program.info.texture_descriptors, | ||
| 205 | program.info.texture_buffer_descriptors, | ||
| 206 | }; | ||
| 189 | for (TextureInst& texture_inst : to_replace) { | 207 | for (TextureInst& texture_inst : to_replace) { |
| 190 | // TODO: Handle arrays | 208 | // TODO: Handle arrays |
| 191 | IR::Inst* const inst{texture_inst.inst}; | 209 | IR::Inst* const inst{texture_inst.inst}; |
| @@ -193,16 +211,42 @@ void TexturePass(Environment& env, IR::Program& program) { | |||
| 193 | 211 | ||
| 194 | const auto& cbuf{texture_inst.cbuf}; | 212 | const auto& cbuf{texture_inst.cbuf}; |
| 195 | auto flags{inst->Flags<IR::TextureInstInfo>()}; | 213 | auto flags{inst->Flags<IR::TextureInstInfo>()}; |
| 196 | if (inst->Opcode() == IR::Opcode::ImageQueryDimensions) { | 214 | switch (inst->Opcode()) { |
| 215 | case IR::Opcode::ImageQueryDimensions: | ||
| 197 | flags.type.Assign(env.ReadTextureType(cbuf.index, cbuf.offset)); | 216 | flags.type.Assign(env.ReadTextureType(cbuf.index, cbuf.offset)); |
| 198 | inst->SetFlags(flags); | 217 | inst->SetFlags(flags); |
| 218 | break; | ||
| 219 | case IR::Opcode::ImageFetch: | ||
| 220 | if (flags.type != TextureType::Color1D) { | ||
| 221 | break; | ||
| 222 | } | ||
| 223 | if (env.ReadTextureType(cbuf.index, cbuf.offset) == TextureType::Buffer) { | ||
| 224 | // Replace with the bound texture type only when it's a texture buffer | ||
| 225 | // If the instruction is 1D and the bound type is 2D, don't change the code and let | ||
| 226 | // the rasterizer robustness handle it | ||
| 227 | // This happens on Fire Emblem: Three Houses | ||
| 228 | flags.type.Assign(TextureType::Buffer); | ||
| 229 | } | ||
| 230 | inst->SetFlags(flags); | ||
| 231 | break; | ||
| 232 | default: | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | u32 index; | ||
| 236 | if (flags.type == TextureType::Buffer) { | ||
| 237 | index = descriptors.Add(TextureBufferDescriptor{ | ||
| 238 | .cbuf_index{cbuf.index}, | ||
| 239 | .cbuf_offset{cbuf.offset}, | ||
| 240 | .count{1}, | ||
| 241 | }); | ||
| 242 | } else { | ||
| 243 | index = descriptors.Add(TextureDescriptor{ | ||
| 244 | .type{flags.type}, | ||
| 245 | .cbuf_index{cbuf.index}, | ||
| 246 | .cbuf_offset{cbuf.offset}, | ||
| 247 | .count{1}, | ||
| 248 | }); | ||
| 199 | } | 249 | } |
| 200 | const u32 index{descriptors.Add(TextureDescriptor{ | ||
| 201 | .type{flags.type}, | ||
| 202 | .cbuf_index{cbuf.index}, | ||
| 203 | .cbuf_offset{cbuf.offset}, | ||
| 204 | .count{1}, | ||
| 205 | })}; | ||
| 206 | inst->SetArg(0, IR::Value{index}); | 250 | inst->SetArg(0, IR::Value{index}); |
| 207 | } | 251 | } |
| 208 | } | 252 | } |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 41bb5b9a1..e6f0de8d8 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -29,6 +29,7 @@ enum class TextureType : u32 { | |||
| 29 | Shadow3D, | 29 | Shadow3D, |
| 30 | ShadowCube, | 30 | ShadowCube, |
| 31 | ShadowArrayCube, | 31 | ShadowArrayCube, |
| 32 | Buffer, | ||
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| 34 | enum class Interpolation { | 35 | enum class Interpolation { |
| @@ -50,6 +51,13 @@ struct TextureDescriptor { | |||
| 50 | }; | 51 | }; |
| 51 | using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; | 52 | using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; |
| 52 | 53 | ||
| 54 | struct TextureBufferDescriptor { | ||
| 55 | u32 cbuf_index; | ||
| 56 | u32 cbuf_offset; | ||
| 57 | u32 count; | ||
| 58 | }; | ||
| 59 | using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 2>; | ||
| 60 | |||
| 53 | struct ConstantBufferDescriptor { | 61 | struct ConstantBufferDescriptor { |
| 54 | u32 index; | 62 | u32 index; |
| 55 | u32 count; | 63 | u32 count; |
| @@ -112,6 +120,7 @@ struct Info { | |||
| 112 | constant_buffer_descriptors; | 120 | constant_buffer_descriptors; |
| 113 | boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; | 121 | boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; |
| 114 | TextureDescriptors texture_descriptors; | 122 | TextureDescriptors texture_descriptors; |
| 123 | TextureBufferDescriptors texture_buffer_descriptors; | ||
| 115 | }; | 124 | }; |
| 116 | 125 | ||
| 117 | } // namespace Shader | 126 | } // namespace Shader |