shader: Implement SULD and SUST
This commit is contained in:
		| @@ -133,6 +133,7 @@ add_library(shader_recompiler STATIC | ||||
|     frontend/maxwell/translate/impl/predicate_set_predicate.cpp | ||||
|     frontend/maxwell/translate/impl/predicate_set_register.cpp | ||||
|     frontend/maxwell/translate/impl/select_source_with_predicate.cpp | ||||
|     frontend/maxwell/translate/impl/surface_load_store.cpp | ||||
|     frontend/maxwell/translate/impl/texture_fetch.cpp | ||||
|     frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp | ||||
|     frontend/maxwell/translate/impl/texture_gather_swizzled.cpp | ||||
|   | ||||
| @@ -18,41 +18,70 @@ namespace { | ||||
| Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | ||||
|     const spv::ImageFormat format{spv::ImageFormat::Unknown}; | ||||
|     const Id type{ctx.F32[1]}; | ||||
|     const bool depth{desc.is_depth}; | ||||
|     switch (desc.type) { | ||||
|     case TextureType::Color1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); | ||||
|     case TextureType::ColorArray1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); | ||||
|     case TextureType::Color2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); | ||||
|     case TextureType::ColorArray2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); | ||||
|     case TextureType::Color3D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); | ||||
|     case TextureType::ColorCube: | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, false, false, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format); | ||||
|     case TextureType::ColorArrayCube: | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, false, true, false, 1, format); | ||||
|     case TextureType::Shadow1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, true, false, false, 1, format); | ||||
|     case TextureType::ShadowArray1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, true, true, false, 1, format); | ||||
|     case TextureType::Shadow2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, true, false, false, 1, format); | ||||
|     case TextureType::ShadowArray2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, true, true, false, 1, format); | ||||
|     case TextureType::Shadow3D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim3D, true, false, false, 1, format); | ||||
|     case TextureType::ShadowCube: | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, true, false, false, 1, format); | ||||
|     case TextureType::ShadowArrayCube: | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, true, true, false, 1, format); | ||||
|         return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format); | ||||
|     case TextureType::Buffer: | ||||
|         break; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid texture type {}", desc.type); | ||||
| } | ||||
|  | ||||
| Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { | ||||
|     const spv::ImageFormat format{[&] { | ||||
|         switch (desc.format) { | ||||
|         case ImageFormat::Typeless: | ||||
|             return spv::ImageFormat::Unknown; | ||||
|         case ImageFormat::R8_UINT: | ||||
|             return spv::ImageFormat::R8ui; | ||||
|         case ImageFormat::R8_SINT: | ||||
|             return spv::ImageFormat::R8i; | ||||
|         case ImageFormat::R16_UINT: | ||||
|             return spv::ImageFormat::R16ui; | ||||
|         case ImageFormat::R16_SINT: | ||||
|             return spv::ImageFormat::R16i; | ||||
|         case ImageFormat::R32_UINT: | ||||
|             return spv::ImageFormat::R32ui; | ||||
|         case ImageFormat::R32G32_UINT: | ||||
|             return spv::ImageFormat::Rg32ui; | ||||
|         case ImageFormat::R32G32B32A32_UINT: | ||||
|             return spv::ImageFormat::Rgba32ui; | ||||
|         } | ||||
|         throw InvalidArgument("Invalid image format {}", desc.format); | ||||
|     }()}; | ||||
|     const Id type{ctx.U32[1]}; | ||||
|     switch (desc.type) { | ||||
|     case TextureType::Color1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format); | ||||
|     case TextureType::ColorArray1D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format); | ||||
|     case TextureType::Color2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format); | ||||
|     case TextureType::ColorArray2D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format); | ||||
|     case TextureType::Color3D: | ||||
|         return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format); | ||||
|     case TextureType::Buffer: | ||||
|         throw NotImplementedException("Image buffer"); | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid texture type {}", desc.type); | ||||
| } | ||||
|  | ||||
| Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin, | ||||
|                   spv::StorageClass storage_class) { | ||||
|     const Id pointer_type{ctx.TypePointer(storage_class, type)}; | ||||
| @@ -134,6 +163,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin | ||||
|     DefineStorageBuffers(program.info, binding); | ||||
|     DefineTextureBuffers(program.info, binding); | ||||
|     DefineTextures(program.info, binding); | ||||
|     DefineImages(program.info, binding); | ||||
|     DefineAttributeMemAccess(program.info); | ||||
|     DefineLabels(program); | ||||
| } | ||||
| @@ -572,6 +602,31 @@ void EmitContext::DefineTextures(const Info& info, u32& binding) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineImages(const Info& info, u32& binding) { | ||||
|     images.reserve(info.image_descriptors.size()); | ||||
|     for (const ImageDescriptor& desc : info.image_descriptors) { | ||||
|         if (desc.count != 1) { | ||||
|             throw NotImplementedException("Array of textures"); | ||||
|         } | ||||
|         const Id image_type{ImageType(*this, desc)}; | ||||
|         const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; | ||||
|         const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; | ||||
|         Decorate(id, spv::Decoration::Binding, binding); | ||||
|         Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||||
|         Name(id, fmt::format("img{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); | ||||
|         for (u32 index = 0; index < desc.count; ++index) { | ||||
|             images.push_back(ImageDefinition{ | ||||
|                 .id{id}, | ||||
|                 .image_type{image_type}, | ||||
|             }); | ||||
|         } | ||||
|         if (profile.supported_spirv >= 0x00010400) { | ||||
|             interfaces.push_back(id); | ||||
|         } | ||||
|         binding += desc.count; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineLabels(IR::Program& program) { | ||||
|     for (IR::Block* const block : program.blocks) { | ||||
|         block->SetDefinition(OpLabel()); | ||||
|   | ||||
| @@ -35,6 +35,11 @@ struct TextureDefinition { | ||||
|     Id image_type; | ||||
| }; | ||||
|  | ||||
| struct ImageDefinition { | ||||
|     Id id; | ||||
|     Id image_type; | ||||
| }; | ||||
|  | ||||
| struct UniformDefinitions { | ||||
|     Id U8{}; | ||||
|     Id S8{}; | ||||
| @@ -95,8 +100,9 @@ public: | ||||
|  | ||||
|     std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; | ||||
|     std::array<Id, Info::MAX_SSBOS> ssbos{}; | ||||
|     std::vector<TextureDefinition> textures; | ||||
|     std::vector<Id> texture_buffers; | ||||
|     std::vector<TextureDefinition> textures; | ||||
|     std::vector<ImageDefinition> images; | ||||
|  | ||||
|     Id workgroup_id{}; | ||||
|     Id local_invocation_id{}; | ||||
| @@ -156,6 +162,7 @@ private: | ||||
|     void DefineStorageBuffers(const Info& info, u32& binding); | ||||
|     void DefineTextureBuffers(const Info& info, u32& binding); | ||||
|     void DefineTextures(const Info& info, u32& binding); | ||||
|     void DefineImages(const Info& info, u32& binding); | ||||
|     void DefineAttributeMemAccess(const Info& info); | ||||
|     void DefineLabels(IR::Program& program); | ||||
|  | ||||
|   | ||||
| @@ -253,6 +253,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | ||||
|     ctx.AddCapability(spv::Capability::ImageGatherExtended); | ||||
|     ctx.AddCapability(spv::Capability::ImageQuery); | ||||
|     ctx.AddCapability(spv::Capability::SampledBuffer); | ||||
|     ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat); | ||||
| } | ||||
|  | ||||
| Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { | ||||
|   | ||||
| @@ -369,6 +369,8 @@ Id EmitBindlessImageFetch(EmitContext&); | ||||
| Id EmitBindlessImageQueryDimensions(EmitContext&); | ||||
| Id EmitBindlessImageQueryLod(EmitContext&); | ||||
| Id EmitBindlessImageGradient(EmitContext&); | ||||
| Id EmitBindlessImageRead(EmitContext&); | ||||
| Id EmitBindlessImageWrite(EmitContext&); | ||||
| Id EmitBoundImageSampleImplicitLod(EmitContext&); | ||||
| Id EmitBoundImageSampleExplicitLod(EmitContext&); | ||||
| Id EmitBoundImageSampleDrefImplicitLod(EmitContext&); | ||||
| @@ -379,6 +381,8 @@ Id EmitBoundImageFetch(EmitContext&); | ||||
| Id EmitBoundImageQueryDimensions(EmitContext&); | ||||
| Id EmitBoundImageQueryLod(EmitContext&); | ||||
| Id EmitBoundImageGradient(EmitContext&); | ||||
| Id EmitBoundImageRead(EmitContext&); | ||||
| Id EmitBoundImageWrite(EmitContext&); | ||||
| Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||||
|                               Id bias_lc, Id offset); | ||||
| Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||||
| @@ -397,6 +401,8 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i | ||||
| Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | ||||
| Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||||
|                      Id derivates, Id offset, Id lod_clamp); | ||||
| Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | ||||
| void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); | ||||
| Id EmitVoteAll(EmitContext& ctx, Id pred); | ||||
| Id EmitVoteAny(EmitContext& ctx, Id pred); | ||||
| Id EmitVoteEqual(EmitContext& ctx, Id pred); | ||||
|   | ||||
| @@ -144,6 +144,18 @@ Id TextureImage(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo in | ||||
|     } | ||||
| } | ||||
|  | ||||
| Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { | ||||
|     if (!index.IsImmediate()) { | ||||
|         throw NotImplementedException("Indirect image indexing"); | ||||
|     } | ||||
|     if (info.type == TextureType::Buffer) { | ||||
|         throw NotImplementedException("Image buffer"); | ||||
|     } else { | ||||
|         const ImageDefinition def{ctx.images.at(index.U32())}; | ||||
|         return ctx.OpLoad(def.image_type, def.id); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { | ||||
|     const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||||
|     if (info.relaxed_precision != 0) { | ||||
| @@ -209,6 +221,14 @@ Id EmitBindlessImageGradient(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitBindlessImageRead(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitBindlessImageWrite(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitBoundImageSampleImplicitLod(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
| @@ -249,6 +269,14 @@ Id EmitBoundImageGradient(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitBoundImageRead(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitBoundImageWrite(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||||
|                               Id bias_lc, Id offset) { | ||||
|     const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||||
| @@ -322,23 +350,16 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i | ||||
|     const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; | ||||
|     switch (info.type) { | ||||
|     case TextureType::Color1D: | ||||
|     case TextureType::Shadow1D: | ||||
|         return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), | ||||
|                                         zero, zero, mips()); | ||||
|     case TextureType::ColorArray1D: | ||||
|     case TextureType::Color2D: | ||||
|     case TextureType::ColorCube: | ||||
|     case TextureType::ShadowArray1D: | ||||
|     case TextureType::Shadow2D: | ||||
|     case TextureType::ShadowCube: | ||||
|         return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), | ||||
|                                         zero, mips()); | ||||
|     case TextureType::ColorArray2D: | ||||
|     case TextureType::Color3D: | ||||
|     case TextureType::ColorArrayCube: | ||||
|     case TextureType::ShadowArray2D: | ||||
|     case TextureType::Shadow3D: | ||||
|     case TextureType::ShadowArrayCube: | ||||
|         return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), | ||||
|                                         mips()); | ||||
|     case TextureType::Buffer: | ||||
| @@ -365,4 +386,15 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I | ||||
|                 coords, operands.Mask(), operands.Span()); | ||||
| } | ||||
|  | ||||
| Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { | ||||
|     const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||||
|     return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4], | ||||
|                 Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{}); | ||||
| } | ||||
|  | ||||
| void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) { | ||||
|     const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||||
|     ctx.OpImageWrite(Image(ctx, index, info), coords, color); | ||||
| } | ||||
|  | ||||
| } // namespace Shader::Backend::SPIRV | ||||
|   | ||||
| @@ -1620,6 +1620,17 @@ Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const V | ||||
|     return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); | ||||
| } | ||||
|  | ||||
| Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { | ||||
|     const Opcode op{handle.IsImmediate() ? Opcode::BoundImageRead : Opcode::BindlessImageRead}; | ||||
|     return Inst(op, Flags{info}, handle, coords); | ||||
| } | ||||
|  | ||||
| void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value& color, | ||||
|                             TextureInstInfo info) { | ||||
|     const Opcode op{handle.IsImmediate() ? Opcode::BoundImageWrite : Opcode::BindlessImageWrite}; | ||||
|     Inst(op, Flags{info}, handle, coords, color); | ||||
| } | ||||
|  | ||||
| U1 IREmitter::VoteAll(const U1& value) { | ||||
|     return Inst<U1>(Opcode::VoteAll, value); | ||||
| } | ||||
|   | ||||
| @@ -265,20 +265,19 @@ public: | ||||
|  | ||||
|     [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, | ||||
|                                       TextureInstInfo info); | ||||
|  | ||||
|     [[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset, | ||||
|                                     const Value& offset2, TextureInstInfo info); | ||||
|  | ||||
|     [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords, | ||||
|                                         const Value& offset, const Value& offset2, const F32& dref, | ||||
|                                         TextureInstInfo info); | ||||
|  | ||||
|     [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, | ||||
|                                    const U32& lod, const U32& multisampling, TextureInstInfo info); | ||||
|  | ||||
|     [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, | ||||
|                                       const Value& derivates, const Value& offset, | ||||
|                                       const F32& lod_clamp, TextureInstInfo info); | ||||
|     [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); | ||||
|     [[nodiscard]] void ImageWrite(const Value& handle, const Value& coords, const Value& color, | ||||
|                                   TextureInstInfo info); | ||||
|  | ||||
|     [[nodiscard]] U1 VoteAll(const U1& value); | ||||
|     [[nodiscard]] U1 VoteAny(const U1& value); | ||||
|   | ||||
| @@ -43,11 +43,13 @@ static_assert(sizeof(FpControl) <= sizeof(u32)); | ||||
| union TextureInstInfo { | ||||
|     u32 raw; | ||||
|     BitField<0, 8, TextureType> type; | ||||
|     BitField<8, 1, u32> has_bias; | ||||
|     BitField<9, 1, u32> has_lod_clamp; | ||||
|     BitField<10, 1, u32> relaxed_precision; | ||||
|     BitField<11, 2, u32> gather_component; | ||||
|     BitField<13, 2, u32> num_derivates; | ||||
|     BitField<8, 1, u32> is_depth; | ||||
|     BitField<9, 1, u32> has_bias; | ||||
|     BitField<10, 1, u32> has_lod_clamp; | ||||
|     BitField<11, 1, u32> relaxed_precision; | ||||
|     BitField<12, 2, u32> gather_component; | ||||
|     BitField<14, 2, u32> num_derivates; | ||||
|     BitField<16, 3, ImageFormat> image_format; | ||||
| }; | ||||
| static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); | ||||
|  | ||||
|   | ||||
| @@ -389,6 +389,8 @@ OPCODE(BindlessImageFetch,                                  F32x4,          U32, | ||||
| OPCODE(BindlessImageQueryDimensions,                        U32x4,          U32,            U32,                                                            ) | ||||
| OPCODE(BindlessImageQueryLod,                               F32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(BindlessImageGradient,                               F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         ) | ||||
| OPCODE(BindlessImageRead,                                   U32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(BindlessImageWrite,                                  Void,           U32,            Opaque,         U32x4,                                          ) | ||||
|  | ||||
| OPCODE(BoundImageSampleImplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         ) | ||||
| OPCODE(BoundImageSampleExplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         ) | ||||
| @@ -400,6 +402,8 @@ OPCODE(BoundImageFetch,                                     F32x4,          U32, | ||||
| OPCODE(BoundImageQueryDimensions,                           U32x4,          U32,            U32,                                                            ) | ||||
| OPCODE(BoundImageQueryLod,                                  F32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(BoundImageGradient,                                  F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         ) | ||||
| OPCODE(BoundImageRead,                                      U32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(BoundImageWrite,                                     Void,           U32,            Opaque,         U32x4,                                          ) | ||||
|  | ||||
| OPCODE(ImageSampleImplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         ) | ||||
| OPCODE(ImageSampleExplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         ) | ||||
| @@ -411,6 +415,8 @@ OPCODE(ImageFetch,                                          F32x4,          U32, | ||||
| OPCODE(ImageQueryDimensions,                                U32x4,          U32,            U32,                                                            ) | ||||
| OPCODE(ImageQueryLod,                                       F32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(ImageGradient,                                       F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         ) | ||||
| OPCODE(ImageRead,                                           U32x4,          U32,            Opaque,                                                         ) | ||||
| OPCODE(ImageWrite,                                          Void,           U32,            Opaque,         U32x4,                                          ) | ||||
|  | ||||
| // Warp operations | ||||
| OPCODE(VoteAll,                                             U1,             U1,                                                                             ) | ||||
|   | ||||
| @@ -281,18 +281,10 @@ void TranslatorVisitor::SUATOM_cas(u64) { | ||||
|     ThrowNotImplemented(Opcode::SUATOM_cas); | ||||
| } | ||||
|  | ||||
| void TranslatorVisitor::SULD(u64) { | ||||
|     ThrowNotImplemented(Opcode::SULD); | ||||
| } | ||||
|  | ||||
| void TranslatorVisitor::SURED(u64) { | ||||
|     ThrowNotImplemented(Opcode::SURED); | ||||
| } | ||||
|  | ||||
| void TranslatorVisitor::SUST(u64) { | ||||
|     ThrowNotImplemented(Opcode::SUST); | ||||
| } | ||||
|  | ||||
| void TranslatorVisitor::SYNC(u64) { | ||||
|     ThrowNotImplemented(Opcode::SYNC); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,280 @@ | ||||
| // Copyright 2021 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <array> | ||||
| #include <bit> | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "shader_recompiler/frontend/ir/modifiers.h" | ||||
| #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||
|  | ||||
| namespace Shader::Maxwell { | ||||
| namespace { | ||||
| enum class Type : u64 { | ||||
|     _1D, | ||||
|     BUFFER_1D, | ||||
|     ARRAY_1D, | ||||
|     _2D, | ||||
|     ARRAY_2D, | ||||
|     _3D, | ||||
| }; | ||||
|  | ||||
| constexpr unsigned R = 1 << 0; | ||||
| constexpr unsigned G = 1 << 1; | ||||
| constexpr unsigned B = 1 << 2; | ||||
| constexpr unsigned A = 1 << 3; | ||||
|  | ||||
| constexpr std::array MASK{ | ||||
|     0U,            // | ||||
|     R,             // | ||||
|     G,             // | ||||
|     R | G,         // | ||||
|     B,             // | ||||
|     R | B,         // | ||||
|     G | B,         // | ||||
|     R | G | B,     // | ||||
|     A,             // | ||||
|     R | A,         // | ||||
|     G | A,         // | ||||
|     R | G | A,     // | ||||
|     B | A,         // | ||||
|     R | B | A,     // | ||||
|     G | B | A,     // | ||||
|     R | G | B | A, // | ||||
| }; | ||||
|  | ||||
| enum class Size : u64 { | ||||
|     U8, | ||||
|     S8, | ||||
|     U16, | ||||
|     S16, | ||||
|     B32, | ||||
|     B64, | ||||
|     B128, | ||||
| }; | ||||
|  | ||||
| enum class Clamp : u64 { | ||||
|     IGN, | ||||
|     Default, | ||||
|     TRAP, | ||||
| }; | ||||
|  | ||||
| enum class LoadCache : u64 { | ||||
|     Default, | ||||
|     CG, | ||||
|     CI, | ||||
|     CV, | ||||
| }; | ||||
|  | ||||
| enum class StoreCache : u64 { | ||||
|     Default, | ||||
|     CG, | ||||
|     CS, | ||||
|     WT, | ||||
| }; | ||||
|  | ||||
| ImageFormat Format(Size size) { | ||||
|     switch (size) { | ||||
|     case Size::U8: | ||||
|         return ImageFormat::R8_UINT; | ||||
|     case Size::S8: | ||||
|         return ImageFormat::R8_SINT; | ||||
|     case Size::U16: | ||||
|         return ImageFormat::R16_UINT; | ||||
|     case Size::S16: | ||||
|         return ImageFormat::R16_SINT; | ||||
|     case Size::B32: | ||||
|         return ImageFormat::R32_UINT; | ||||
|     case Size::B64: | ||||
|         return ImageFormat::R32G32_UINT; | ||||
|     case Size::B128: | ||||
|         return ImageFormat::R32G32B32A32_UINT; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid size {}", size); | ||||
| } | ||||
|  | ||||
| int SizeInRegs(Size size) { | ||||
|     switch (size) { | ||||
|     case Size::U8: | ||||
|     case Size::S8: | ||||
|     case Size::U16: | ||||
|     case Size::S16: | ||||
|     case Size::B32: | ||||
|         return 1; | ||||
|     case Size::B64: | ||||
|         return 2; | ||||
|     case Size::B128: | ||||
|         return 4; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid size {}", size); | ||||
| } | ||||
|  | ||||
| TextureType GetType(Type type) { | ||||
|     switch (type) { | ||||
|     case Type::_1D: | ||||
|         return TextureType::Color1D; | ||||
|     case Type::BUFFER_1D: | ||||
|         return TextureType::Buffer; | ||||
|     case Type::ARRAY_1D: | ||||
|         return TextureType::ColorArray1D; | ||||
|     case Type::_2D: | ||||
|         return TextureType::Color2D; | ||||
|     case Type::ARRAY_2D: | ||||
|         return TextureType::ColorArray2D; | ||||
|     case Type::_3D: | ||||
|         return TextureType::Color3D; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid type {}", type); | ||||
| } | ||||
|  | ||||
| IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) { | ||||
|     const auto array{[&](int index) { | ||||
|         return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16)); | ||||
|     }}; | ||||
|     switch (type) { | ||||
|     case Type::_1D: | ||||
|     case Type::BUFFER_1D: | ||||
|         return v.X(reg); | ||||
|     case Type::ARRAY_1D: | ||||
|         return v.ir.CompositeConstruct(v.X(reg), array(1)); | ||||
|     case Type::_2D: | ||||
|         return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1)); | ||||
|     case Type::ARRAY_2D: | ||||
|         return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2)); | ||||
|     case Type::_3D: | ||||
|         return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 3)); | ||||
|     } | ||||
|     throw NotImplementedException("Invalid type {}", type); | ||||
| } | ||||
|  | ||||
| unsigned SwizzleMask(u64 swizzle) { | ||||
|     if (swizzle == 0 || swizzle >= MASK.size()) { | ||||
|         throw NotImplementedException("Invalid swizzle {}", swizzle); | ||||
|     } | ||||
|     return MASK[swizzle]; | ||||
| } | ||||
|  | ||||
| IR::Value MakeColor(IR::IREmitter& ir, IR::Reg reg, int num_regs) { | ||||
|     std::array<IR::U32, 4> colors; | ||||
|     for (int i = 0; i < num_regs; ++i) { | ||||
|         colors[i] = ir.GetReg(reg + i); | ||||
|     } | ||||
|     for (int i = num_regs; i < 4; ++i) { | ||||
|         colors[i] = ir.Imm32(0); | ||||
|     } | ||||
|     return ir.CompositeConstruct(colors[0], colors[1], colors[2], colors[3]); | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| void TranslatorVisitor::SULD(u64 insn) { | ||||
|     union { | ||||
|         u64 raw; | ||||
|         BitField<51, 1, u64> is_bound; | ||||
|         BitField<52, 1, u64> d; | ||||
|         BitField<23, 1, u64> ba; | ||||
|         BitField<33, 3, Type> type; | ||||
|         BitField<24, 2, LoadCache> cache; | ||||
|         BitField<20, 3, Size> size;   // .D | ||||
|         BitField<20, 4, u64> swizzle; // .P | ||||
|         BitField<49, 2, Clamp> clamp; | ||||
|         BitField<0, 8, IR::Reg> dest_reg; | ||||
|         BitField<8, 8, IR::Reg> coord_reg; | ||||
|         BitField<36, 13, u64> bound_offset;    // is_bound | ||||
|         BitField<39, 8, IR::Reg> bindless_reg; // !is_bound | ||||
|     } const suld{insn}; | ||||
|  | ||||
|     if (suld.clamp != Clamp::IGN) { | ||||
|         throw NotImplementedException("Clamp {}", suld.clamp.Value()); | ||||
|     } | ||||
|     if (suld.cache != LoadCache::Default) { | ||||
|         throw NotImplementedException("Cache {}", suld.cache.Value()); | ||||
|     } | ||||
|     const bool is_typed{suld.d != 0}; | ||||
|     if (is_typed && suld.ba != 0) { | ||||
|         throw NotImplementedException("BA"); | ||||
|     } | ||||
|  | ||||
|     const ImageFormat format{is_typed ? Format(suld.size) : ImageFormat::Typeless}; | ||||
|     const TextureType type{GetType(suld.type)}; | ||||
|     const IR::Value coords{MakeCoords(*this, suld.coord_reg, suld.type)}; | ||||
|     const IR::U32 handle{suld.is_bound != 0 ? ir.Imm32(static_cast<u32>(suld.bound_offset * 4)) | ||||
|                                             : X(suld.bindless_reg)}; | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(type); | ||||
|     info.image_format.Assign(format); | ||||
|  | ||||
|     const IR::Value result{ir.ImageRead(handle, coords, info)}; | ||||
|     IR::Reg dest_reg{suld.dest_reg}; | ||||
|     if (is_typed) { | ||||
|         const int num_regs{SizeInRegs(suld.size)}; | ||||
|         for (int i = 0; i < num_regs; ++i) { | ||||
|             X(dest_reg + i, IR::U32{ir.CompositeExtract(result, i)}); | ||||
|         } | ||||
|     } else { | ||||
|         const unsigned mask{SwizzleMask(suld.swizzle)}; | ||||
|         const int bits{std::popcount(mask)}; | ||||
|         if (!IR::IsAligned(dest_reg, bits == 3 ? 4 : bits)) { | ||||
|             throw NotImplementedException("Unaligned destination register"); | ||||
|         } | ||||
|         for (unsigned component = 0; component < 4; ++component) { | ||||
|             if (((mask >> component) & 1) == 0) { | ||||
|                 continue; | ||||
|             } | ||||
|             X(dest_reg, IR::U32{ir.CompositeExtract(result, component)}); | ||||
|             ++dest_reg; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void TranslatorVisitor::SUST(u64 insn) { | ||||
|     union { | ||||
|         u64 raw; | ||||
|         BitField<51, 1, u64> is_bound; | ||||
|         BitField<52, 1, u64> d; | ||||
|         BitField<23, 1, u64> ba; | ||||
|         BitField<33, 3, Type> type; | ||||
|         BitField<24, 2, StoreCache> cache; | ||||
|         BitField<20, 3, Size> size;   // .D | ||||
|         BitField<20, 4, u64> swizzle; // .P | ||||
|         BitField<49, 2, Clamp> clamp; | ||||
|         BitField<0, 8, IR::Reg> data_reg; | ||||
|         BitField<8, 8, IR::Reg> coord_reg; | ||||
|         BitField<36, 13, u64> bound_offset;    // is_bound | ||||
|         BitField<39, 8, IR::Reg> bindless_reg; // !is_bound | ||||
|     } const sust{insn}; | ||||
|  | ||||
|     if (sust.clamp != Clamp::IGN) { | ||||
|         throw NotImplementedException("Clamp {}", sust.clamp.Value()); | ||||
|     } | ||||
|     if (sust.cache != StoreCache::Default) { | ||||
|         throw NotImplementedException("Cache {}", sust.cache.Value()); | ||||
|     } | ||||
|     const bool is_typed{sust.d != 0}; | ||||
|     if (is_typed && sust.ba != 0) { | ||||
|         throw NotImplementedException("BA"); | ||||
|     } | ||||
|     const ImageFormat format{is_typed ? Format(sust.size) : ImageFormat::Typeless}; | ||||
|     const TextureType type{GetType(sust.type)}; | ||||
|     const IR::Value coords{MakeCoords(*this, sust.coord_reg, sust.type)}; | ||||
|     const IR::U32 handle{sust.is_bound != 0 ? ir.Imm32(static_cast<u32>(sust.bound_offset * 4)) | ||||
|                                             : X(sust.bindless_reg)}; | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(type); | ||||
|     info.image_format.Assign(format); | ||||
|  | ||||
|     IR::Value color; | ||||
|     if (is_typed) { | ||||
|         color = MakeColor(ir, sust.data_reg, SizeInRegs(sust.size)); | ||||
|     } else { | ||||
|         const unsigned mask{SwizzleMask(sust.swizzle)}; | ||||
|         if (mask != 0xf) { | ||||
|             throw NotImplementedException("Non-full mask"); | ||||
|         } | ||||
|         color = MakeColor(ir, sust.data_reg, 4); | ||||
|     } | ||||
|     ir.ImageWrite(handle, coords, color, info); | ||||
| } | ||||
|  | ||||
| } // namespace Shader::Maxwell | ||||
| @@ -33,24 +33,24 @@ enum class TextureType : u64 { | ||||
|     ARRAY_CUBE, | ||||
| }; | ||||
|  | ||||
| Shader::TextureType GetType(TextureType type, bool dc) { | ||||
| Shader::TextureType GetType(TextureType type) { | ||||
|     switch (type) { | ||||
|     case TextureType::_1D: | ||||
|         return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D; | ||||
|         return Shader::TextureType::Color1D; | ||||
|     case TextureType::ARRAY_1D: | ||||
|         return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D; | ||||
|         return Shader::TextureType::ColorArray1D; | ||||
|     case TextureType::_2D: | ||||
|         return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D; | ||||
|         return Shader::TextureType::Color2D; | ||||
|     case TextureType::ARRAY_2D: | ||||
|         return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D; | ||||
|         return Shader::TextureType::ColorArray2D; | ||||
|     case TextureType::_3D: | ||||
|         return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D; | ||||
|         return Shader::TextureType::Color3D; | ||||
|     case TextureType::ARRAY_3D: | ||||
|         throw NotImplementedException("3D array texture type"); | ||||
|     case TextureType::CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube; | ||||
|         return Shader::TextureType::ColorCube; | ||||
|     case TextureType::ARRAY_CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube; | ||||
|         return Shader::TextureType::ColorArrayCube; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid texture type {}", type); | ||||
| } | ||||
| @@ -169,7 +169,8 @@ void Impl(TranslatorVisitor& v, u64 insn, bool aoffi, Blod blod, bool lc, | ||||
|         dref = v.F(meta_reg++); | ||||
|     } | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(GetType(tex.type, tex.dc != 0)); | ||||
|     info.type.Assign(GetType(tex.type)); | ||||
|     info.is_depth.Assign(tex.dc != 0 ? 1 : 0); | ||||
|     info.has_bias.Assign(blod == Blod::LB || blod == Blod::LBA ? 1 : 0); | ||||
|     info.has_lod_clamp.Assign(lc ? 1 : 0); | ||||
|  | ||||
|   | ||||
| @@ -95,18 +95,21 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { | ||||
|                                            {}, info); | ||||
|     case 4: // 2D.DC | ||||
|         CheckAlignment(reg_a, 2); | ||||
|         info.type.Assign(TextureType::Shadow2D); | ||||
|         info.type.Assign(TextureType::Color2D); | ||||
|         info.is_depth.Assign(1); | ||||
|         return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), | ||||
|                                                {}, {}, {}, info); | ||||
|     case 5: // 2D.LL.DC | ||||
|         CheckAlignment(reg_a, 2); | ||||
|         CheckAlignment(reg_b, 2); | ||||
|         info.type.Assign(TextureType::Shadow2D); | ||||
|         info.type.Assign(TextureType::Color2D); | ||||
|         info.is_depth.Assign(1); | ||||
|         return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), | ||||
|                                                v.F(reg_b + 1), v.F(reg_b), {}, {}, info); | ||||
|     case 6: // 2D.LZ.DC | ||||
|         CheckAlignment(reg_a, 2); | ||||
|         info.type.Assign(TextureType::Shadow2D); | ||||
|         info.type.Assign(TextureType::Color2D); | ||||
|         info.is_depth.Assign(1); | ||||
|         return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), | ||||
|                                                zero, {}, {}, info); | ||||
|     case 7: // ARRAY_2D | ||||
| @@ -124,7 +127,8 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { | ||||
|     case 9: // ARRAY_2D.LZ.DC | ||||
|         CheckAlignment(reg_a, 2); | ||||
|         CheckAlignment(reg_b, 2); | ||||
|         info.type.Assign(TextureType::ShadowArray2D); | ||||
|         info.type.Assign(TextureType::ColorArray2D); | ||||
|         info.is_depth.Assign(1); | ||||
|         return v.ir.ImageSampleDrefExplicitLod( | ||||
|             handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), | ||||
|             v.F(reg_b + 1), zero, {}, {}, info); | ||||
|   | ||||
| @@ -37,24 +37,24 @@ enum class ComponentType : u64 { | ||||
|     A = 3, | ||||
| }; | ||||
|  | ||||
| Shader::TextureType GetType(TextureType type, bool dc) { | ||||
| Shader::TextureType GetType(TextureType type) { | ||||
|     switch (type) { | ||||
|     case TextureType::_1D: | ||||
|         return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D; | ||||
|         return Shader::TextureType::Color1D; | ||||
|     case TextureType::ARRAY_1D: | ||||
|         return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D; | ||||
|         return Shader::TextureType::ColorArray1D; | ||||
|     case TextureType::_2D: | ||||
|         return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D; | ||||
|         return Shader::TextureType::Color2D; | ||||
|     case TextureType::ARRAY_2D: | ||||
|         return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D; | ||||
|         return Shader::TextureType::ColorArray2D; | ||||
|     case TextureType::_3D: | ||||
|         return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D; | ||||
|         return Shader::TextureType::Color3D; | ||||
|     case TextureType::ARRAY_3D: | ||||
|         throw NotImplementedException("3D array texture type"); | ||||
|     case TextureType::CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube; | ||||
|         return Shader::TextureType::ColorCube; | ||||
|     case TextureType::ARRAY_CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube; | ||||
|         return Shader::TextureType::ColorArrayCube; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid texture type {}", type); | ||||
| } | ||||
| @@ -163,7 +163,8 @@ void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetTy | ||||
|         dref = v.F(meta_reg++); | ||||
|     } | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(GetType(tld4.type, tld4.dc != 0)); | ||||
|     info.type.Assign(GetType(tld4.type)); | ||||
|     info.is_depth.Assign(tld4.dc != 0 ? 1 : 0); | ||||
|     info.gather_component.Assign(static_cast<u32>(component_type)); | ||||
|     const IR::Value sample{[&] { | ||||
|         if (tld4.dc == 0) { | ||||
|   | ||||
| @@ -59,7 +59,8 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { | ||||
|         info.relaxed_precision.Assign(1); | ||||
|     } | ||||
|     info.gather_component.Assign(static_cast<u32>(tld4s.component_type.Value())); | ||||
|     info.type.Assign(tld4s.dc != 0 ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D); | ||||
|     info.type.Assign(Shader::TextureType::Color2D); | ||||
|     info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0); | ||||
|     IR::Value coords; | ||||
|     if (tld4s.aoffi != 0) { | ||||
|         CheckAlignment(reg_a, 2); | ||||
|   | ||||
| @@ -23,24 +23,24 @@ enum class TextureType : u64 { | ||||
|     ARRAY_CUBE, | ||||
| }; | ||||
|  | ||||
| Shader::TextureType GetType(TextureType type, bool dc) { | ||||
| Shader::TextureType GetType(TextureType type) { | ||||
|     switch (type) { | ||||
|     case TextureType::_1D: | ||||
|         return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D; | ||||
|         return Shader::TextureType::Color1D; | ||||
|     case TextureType::ARRAY_1D: | ||||
|         return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D; | ||||
|         return Shader::TextureType::ColorArray1D; | ||||
|     case TextureType::_2D: | ||||
|         return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D; | ||||
|         return Shader::TextureType::Color2D; | ||||
|     case TextureType::ARRAY_2D: | ||||
|         return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D; | ||||
|         return Shader::TextureType::ColorArray2D; | ||||
|     case TextureType::_3D: | ||||
|         return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D; | ||||
|         return Shader::TextureType::Color3D; | ||||
|     case TextureType::ARRAY_3D: | ||||
|         throw NotImplementedException("3D array texture type"); | ||||
|     case TextureType::CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube; | ||||
|         return Shader::TextureType::ColorCube; | ||||
|     case TextureType::ARRAY_CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube; | ||||
|         return Shader::TextureType::ColorArrayCube; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid texture type {}", type); | ||||
| } | ||||
| @@ -152,7 +152,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | ||||
|     } | ||||
|  | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(GetType(txd.type, false)); | ||||
|     info.type.Assign(GetType(txd.type)); | ||||
|     info.num_derivates.Assign(num_derivates); | ||||
|     info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); | ||||
|     const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; | ||||
|   | ||||
| @@ -23,24 +23,24 @@ enum class TextureType : u64 { | ||||
|     ARRAY_CUBE, | ||||
| }; | ||||
|  | ||||
| Shader::TextureType GetType(TextureType type, bool dc) { | ||||
| Shader::TextureType GetType(TextureType type) { | ||||
|     switch (type) { | ||||
|     case TextureType::_1D: | ||||
|         return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D; | ||||
|         return Shader::TextureType::Color1D; | ||||
|     case TextureType::ARRAY_1D: | ||||
|         return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D; | ||||
|         return Shader::TextureType::ColorArray1D; | ||||
|     case TextureType::_2D: | ||||
|         return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D; | ||||
|         return Shader::TextureType::Color2D; | ||||
|     case TextureType::ARRAY_2D: | ||||
|         return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D; | ||||
|         return Shader::TextureType::ColorArray2D; | ||||
|     case TextureType::_3D: | ||||
|         return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D; | ||||
|         return Shader::TextureType::Color3D; | ||||
|     case TextureType::ARRAY_3D: | ||||
|         throw NotImplementedException("3D array texture type"); | ||||
|     case TextureType::CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube; | ||||
|         return Shader::TextureType::ColorCube; | ||||
|     case TextureType::ARRAY_CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube; | ||||
|         return Shader::TextureType::ColorArrayCube; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid texture type {}", type); | ||||
| } | ||||
| @@ -137,7 +137,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | ||||
|         throw NotImplementedException("TLD.CL - CLAMP is not implmented"); | ||||
|     } | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(GetType(tld.type, false)); | ||||
|     info.type.Assign(GetType(tld.type)); | ||||
|     const IR::Value sample{v.ir.ImageFetch(handle, coords, offset, lod, multisample, info)}; | ||||
|  | ||||
|     IR::Reg dest_reg{tld.dest_reg}; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <utility> | ||||
| #include <array> | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
|   | ||||
| @@ -23,24 +23,24 @@ enum class TextureType : u64 { | ||||
|     ARRAY_CUBE, | ||||
| }; | ||||
|  | ||||
| Shader::TextureType GetType(TextureType type, bool dc) { | ||||
| Shader::TextureType GetType(TextureType type) { | ||||
|     switch (type) { | ||||
|     case TextureType::_1D: | ||||
|         return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D; | ||||
|         return Shader::TextureType::Color1D; | ||||
|     case TextureType::ARRAY_1D: | ||||
|         return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D; | ||||
|         return Shader::TextureType::ColorArray1D; | ||||
|     case TextureType::_2D: | ||||
|         return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D; | ||||
|         return Shader::TextureType::Color2D; | ||||
|     case TextureType::ARRAY_2D: | ||||
|         return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D; | ||||
|         return Shader::TextureType::ColorArray2D; | ||||
|     case TextureType::_3D: | ||||
|         return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D; | ||||
|         return Shader::TextureType::Color3D; | ||||
|     case TextureType::ARRAY_3D: | ||||
|         throw NotImplementedException("3D array texture type"); | ||||
|     case TextureType::CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube; | ||||
|         return Shader::TextureType::ColorCube; | ||||
|     case TextureType::ARRAY_CUBE: | ||||
|         return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube; | ||||
|         return Shader::TextureType::ColorArrayCube; | ||||
|     } | ||||
|     throw NotImplementedException("Invalid texture type {}", type); | ||||
| } | ||||
| @@ -97,7 +97,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | ||||
|         handle = v.ir.Imm32(static_cast<u32>(tmml.cbuf_offset.Value() * 4)); | ||||
|     } | ||||
|     IR::TextureInstInfo info{}; | ||||
|     info.type.Assign(GetType(tmml.type, false)); | ||||
|     info.type.Assign(GetType(tmml.type)); | ||||
|     const IR::Value sample{v.ir.ImageQueryLod(handle, coords, info)}; | ||||
|  | ||||
|     IR::Reg dest_reg{tmml.dest_reg}; | ||||
|   | ||||
| @@ -416,8 +416,7 @@ void VisitUsages(Info& info, IR::Inst& inst) { | ||||
|     case IR::Opcode::ImageQueryLod: | ||||
|     case IR::Opcode::ImageGradient: { | ||||
|         const TextureType type{inst.Flags<IR::TextureInstInfo>().type}; | ||||
|         info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D || | ||||
|                                 type == TextureType::Shadow1D || type == TextureType::ShadowArray1D; | ||||
|         info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D; | ||||
|         info.uses_sparse_residency |= | ||||
|             inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr; | ||||
|         break; | ||||
|   | ||||
| @@ -61,6 +61,12 @@ IR::Opcode IndexedInstruction(const IR::Inst& inst) { | ||||
|     case IR::Opcode::BoundImageGradient: | ||||
|     case IR::Opcode::BindlessImageGradient: | ||||
|         return IR::Opcode::ImageGradient; | ||||
|     case IR::Opcode::BoundImageRead: | ||||
|     case IR::Opcode::BindlessImageRead: | ||||
|         return IR::Opcode::ImageRead; | ||||
|     case IR::Opcode::BoundImageWrite: | ||||
|     case IR::Opcode::BindlessImageWrite: | ||||
|         return IR::Opcode::ImageWrite; | ||||
|     default: | ||||
|         return IR::Opcode::Void; | ||||
|     } | ||||
| @@ -78,6 +84,8 @@ bool IsBindless(const IR::Inst& inst) { | ||||
|     case IR::Opcode::BindlessImageQueryDimensions: | ||||
|     case IR::Opcode::BindlessImageQueryLod: | ||||
|     case IR::Opcode::BindlessImageGradient: | ||||
|     case IR::Opcode::BindlessImageRead: | ||||
|     case IR::Opcode::BindlessImageWrite: | ||||
|         return true; | ||||
|     case IR::Opcode::BoundImageSampleImplicitLod: | ||||
|     case IR::Opcode::BoundImageSampleExplicitLod: | ||||
| @@ -89,6 +97,8 @@ bool IsBindless(const IR::Inst& inst) { | ||||
|     case IR::Opcode::BoundImageQueryDimensions: | ||||
|     case IR::Opcode::BoundImageQueryLod: | ||||
|     case IR::Opcode::BoundImageGradient: | ||||
|     case IR::Opcode::BoundImageRead: | ||||
|     case IR::Opcode::BoundImageWrite: | ||||
|         return false; | ||||
|     default: | ||||
|         throw InvalidArgument("Invalid opcode {}", inst.GetOpcode()); | ||||
| @@ -147,10 +157,18 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { | ||||
|  | ||||
| class Descriptors { | ||||
| public: | ||||
|     explicit Descriptors(TextureDescriptors& texture_descriptors_, | ||||
|                          TextureBufferDescriptors& texture_buffer_descriptors_) | ||||
|         : texture_descriptors{texture_descriptors_}, texture_buffer_descriptors{ | ||||
|                                                          texture_buffer_descriptors_} {} | ||||
|     explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, | ||||
|                          TextureDescriptors& texture_descriptors_, | ||||
|                          ImageDescriptors& image_descriptors_) | ||||
|         : texture_buffer_descriptors{texture_buffer_descriptors_}, | ||||
|           texture_descriptors{texture_descriptors_}, image_descriptors{image_descriptors_} {} | ||||
|  | ||||
|     u32 Add(const TextureBufferDescriptor& desc) { | ||||
|         return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { | ||||
|             return desc.cbuf_index == existing.cbuf_index && | ||||
|                    desc.cbuf_offset == existing.cbuf_offset; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     u32 Add(const TextureDescriptor& desc) { | ||||
|         return Add(texture_descriptors, desc, [&desc](const auto& existing) { | ||||
| @@ -159,11 +177,14 @@ public: | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     u32 Add(const TextureBufferDescriptor& desc) { | ||||
|         return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { | ||||
|             return desc.cbuf_index == existing.cbuf_index && | ||||
|     u32 Add(const ImageDescriptor& desc) { | ||||
|         const u32 index{Add(image_descriptors, desc, [&desc](const auto& existing) { | ||||
|             return desc.type == existing.type && desc.format == existing.format && | ||||
|                    desc.cbuf_index == existing.cbuf_index && | ||||
|                    desc.cbuf_offset == existing.cbuf_offset; | ||||
|         }); | ||||
|         })}; | ||||
|         image_descriptors[index].is_written |= desc.is_written; | ||||
|         return index; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| @@ -178,8 +199,9 @@ private: | ||||
|         return static_cast<u32>(descriptors.size()) - 1; | ||||
|     } | ||||
|  | ||||
|     TextureDescriptors& texture_descriptors; | ||||
|     TextureBufferDescriptors& texture_buffer_descriptors; | ||||
|     TextureDescriptors& texture_descriptors; | ||||
|     ImageDescriptors& image_descriptors; | ||||
| }; | ||||
| } // Anonymous namespace | ||||
|  | ||||
| @@ -201,8 +223,9 @@ void TexturePass(Environment& env, IR::Program& program) { | ||||
|         return lhs.cbuf.index < rhs.cbuf.index; | ||||
|     }); | ||||
|     Descriptors descriptors{ | ||||
|         program.info.texture_descriptors, | ||||
|         program.info.texture_buffer_descriptors, | ||||
|         program.info.texture_descriptors, | ||||
|         program.info.image_descriptors, | ||||
|     }; | ||||
|     for (TextureInst& texture_inst : to_replace) { | ||||
|         // TODO: Handle arrays | ||||
| @@ -233,19 +256,41 @@ void TexturePass(Environment& env, IR::Program& program) { | ||||
|             break; | ||||
|         } | ||||
|         u32 index; | ||||
|         if (flags.type == TextureType::Buffer) { | ||||
|             index = descriptors.Add(TextureBufferDescriptor{ | ||||
|                 .cbuf_index = cbuf.index, | ||||
|                 .cbuf_offset = cbuf.offset, | ||||
|                 .count = 1, | ||||
|             }); | ||||
|         } else { | ||||
|             index = descriptors.Add(TextureDescriptor{ | ||||
|                 .type = flags.type, | ||||
|                 .cbuf_index = cbuf.index, | ||||
|                 .cbuf_offset = cbuf.offset, | ||||
|                 .count = 1, | ||||
|             }); | ||||
|         switch (inst->GetOpcode()) { | ||||
|         case IR::Opcode::ImageRead: | ||||
|         case IR::Opcode::ImageWrite: { | ||||
|             const bool is_written{inst->GetOpcode() == IR::Opcode::ImageWrite}; | ||||
|             if (flags.type == TextureType::Buffer) { | ||||
|                 throw NotImplementedException("Image buffer"); | ||||
|             } else { | ||||
|                 index = descriptors.Add(ImageDescriptor{ | ||||
|                     .type = flags.type, | ||||
|                     .format = flags.image_format, | ||||
|                     .is_written = is_written, | ||||
|                     .cbuf_index = cbuf.index, | ||||
|                     .cbuf_offset = cbuf.offset, | ||||
|                     .count = 1, | ||||
|                 }); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             if (flags.type == TextureType::Buffer) { | ||||
|                 index = descriptors.Add(TextureBufferDescriptor{ | ||||
|                     .cbuf_index = cbuf.index, | ||||
|                     .cbuf_offset = cbuf.offset, | ||||
|                     .count = 1, | ||||
|                 }); | ||||
|             } else { | ||||
|                 index = descriptors.Add(TextureDescriptor{ | ||||
|                     .type = flags.type, | ||||
|                     .is_depth = flags.is_depth != 0, | ||||
|                     .cbuf_index = cbuf.index, | ||||
|                     .cbuf_offset = cbuf.offset, | ||||
|                     .count = 1, | ||||
|                 }); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         inst->SetArg(0, IR::Value{index}); | ||||
|     } | ||||
|   | ||||
| @@ -22,15 +22,20 @@ enum class TextureType : u32 { | ||||
|     Color3D, | ||||
|     ColorCube, | ||||
|     ColorArrayCube, | ||||
|     Shadow1D, | ||||
|     ShadowArray1D, | ||||
|     Shadow2D, | ||||
|     ShadowArray2D, | ||||
|     Shadow3D, | ||||
|     ShadowCube, | ||||
|     ShadowArrayCube, | ||||
|     Buffer, | ||||
| }; | ||||
| constexpr u32 NUM_TEXTURE_TYPES = 8; | ||||
|  | ||||
| enum class ImageFormat : u32 { | ||||
|     Typeless, | ||||
|     R8_UINT, | ||||
|     R8_SINT, | ||||
|     R16_UINT, | ||||
|     R16_SINT, | ||||
|     R32_UINT, | ||||
|     R32G32_UINT, | ||||
|     R32G32B32A32_UINT, | ||||
| }; | ||||
|  | ||||
| enum class Interpolation { | ||||
|     Smooth, | ||||
| @@ -43,21 +48,6 @@ struct InputVarying { | ||||
|     bool used{false}; | ||||
| }; | ||||
|  | ||||
| struct TextureDescriptor { | ||||
|     TextureType type; | ||||
|     u32 cbuf_index; | ||||
|     u32 cbuf_offset; | ||||
|     u32 count; | ||||
| }; | ||||
| using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; | ||||
|  | ||||
| struct TextureBufferDescriptor { | ||||
|     u32 cbuf_index; | ||||
|     u32 cbuf_offset; | ||||
|     u32 count; | ||||
| }; | ||||
| using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 2>; | ||||
|  | ||||
| struct ConstantBufferDescriptor { | ||||
|     u32 index; | ||||
|     u32 count; | ||||
| @@ -70,6 +60,32 @@ struct StorageBufferDescriptor { | ||||
|     bool is_written; | ||||
| }; | ||||
|  | ||||
| struct TextureBufferDescriptor { | ||||
|     u32 cbuf_index; | ||||
|     u32 cbuf_offset; | ||||
|     u32 count; | ||||
| }; | ||||
| using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; | ||||
|  | ||||
| struct TextureDescriptor { | ||||
|     TextureType type; | ||||
|     bool is_depth; | ||||
|     u32 cbuf_index; | ||||
|     u32 cbuf_offset; | ||||
|     u32 count; | ||||
| }; | ||||
| using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; | ||||
|  | ||||
| struct ImageDescriptor { | ||||
|     TextureType type; | ||||
|     ImageFormat format; | ||||
|     bool is_written; | ||||
|     u32 cbuf_index; | ||||
|     u32 cbuf_offset; | ||||
|     u32 count; | ||||
| }; | ||||
| using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; | ||||
|  | ||||
| struct Info { | ||||
|     static constexpr size_t MAX_CBUFS{18}; | ||||
|     static constexpr size_t MAX_SSBOS{16}; | ||||
| @@ -121,6 +137,7 @@ struct Info { | ||||
|     boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; | ||||
|     TextureBufferDescriptors texture_buffer_descriptors; | ||||
|     TextureDescriptors texture_descriptors; | ||||
|     ImageDescriptors image_descriptors; | ||||
| }; | ||||
|  | ||||
| } // namespace Shader | ||||
|   | ||||
		Reference in New Issue
	
	Block a user