diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 5cd505d99d..c8ce582543 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -182,6 +182,7 @@ void EmitContext::DefineCommonConstants() {
     true_value = ConstantTrue(U1);
     false_value = ConstantFalse(U1);
     u32_zero_value = Constant(U32[1], 0U);
+    f32_zero_value = Constant(F32[1], 0.0f);
 }
 
 void EmitContext::DefineInterfaces(const Info& info) {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 071e66c2ae..3965869f04 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -70,6 +70,7 @@ public:
     Id true_value{};
     Id false_value{};
     Id u32_zero_value{};
+    Id f32_zero_value{};
 
     UniformDefinitions uniform_types;
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 3d0c6f7ba4..105c237452 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -362,6 +362,7 @@ Id EmitBindlessImageGather(EmitContext&);
 Id EmitBindlessImageGatherDref(EmitContext&);
 Id EmitBindlessImageFetch(EmitContext&);
 Id EmitBindlessImageQueryDimensions(EmitContext&);
+Id EmitBindlessImageQueryLod(EmitContext&);
 Id EmitBoundImageSampleImplicitLod(EmitContext&);
 Id EmitBoundImageSampleExplicitLod(EmitContext&);
 Id EmitBoundImageSampleDrefImplicitLod(EmitContext&);
@@ -370,6 +371,7 @@ Id EmitBoundImageGather(EmitContext&);
 Id EmitBoundImageGatherDref(EmitContext&);
 Id EmitBoundImageFetch(EmitContext&);
 Id EmitBoundImageQueryDimensions(EmitContext&);
+Id EmitBoundImageQueryLod(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,
@@ -385,6 +387,7 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
 Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
                   Id lod, Id ms);
 Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod);
+Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
 Id EmitVoteAll(EmitContext& ctx, Id pred);
 Id EmitVoteAny(EmitContext& ctx, Id pred);
 Id EmitVoteEqual(EmitContext& ctx, Id pred);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 310cc7af7e..2cd6b38c46 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -161,6 +161,10 @@ Id EmitBindlessImageQueryDimensions(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
+Id EmitBindlessImageQueryLod(EmitContext&) {
+    throw LogicError("Unreachable instruction");
+}
+
 Id EmitBoundImageSampleImplicitLod(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
@@ -193,6 +197,10 @@ Id EmitBoundImageQueryDimensions(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
+Id EmitBoundImageQueryLod(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>()};
@@ -287,4 +295,11 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
     throw LogicError("Unspecified image type {}", info.type.Value());
 }
 
+Id EmitImageQueryLod(EmitContext& ctx, IR::Inst*, const IR::Value& index, Id coords) {
+    const Id zero{ctx.f32_zero_value};
+    const Id image{TextureImage(ctx, index)};
+    return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], image, coords),
+                                    zero, zero);
+}
+
 } // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 6e7dddeada..ba95917277 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1567,6 +1567,12 @@ Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) {
     return Inst(op, handle, lod);
 }
 
+Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords) {
+    const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryLod
+                                         : Opcode::BindlessImageQueryLod};
+    return Inst(op, handle, coords);
+}
+
 U1 IREmitter::VoteAll(const U1& value) {
     return Inst<U1>(Opcode::VoteAll, value);
 }
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 8f3325738c..9e752b2083 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -255,6 +255,8 @@ public:
                                                  TextureInstInfo info);
     [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
 
+    [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords);
+
     [[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
                                     const Value& offset2, TextureInstInfo info);
 
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 302b8471df..49cdcd57ff 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -380,6 +380,7 @@ OPCODE(BindlessImageGather,                                 F32x4,          U32,
 OPCODE(BindlessImageGatherDref,                             F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
 OPCODE(BindlessImageFetch,                                  F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         )
 OPCODE(BindlessImageQueryDimensions,                        U32x4,          U32,            U32,                                                            )
+OPCODE(BindlessImageQueryLod,                               F32x4,          U32,            Opaque,                                                         )
 
 OPCODE(BoundImageSampleImplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(BoundImageSampleExplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
@@ -389,6 +390,7 @@ OPCODE(BoundImageGather,                                    F32x4,          U32,
 OPCODE(BoundImageGatherDref,                                F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
 OPCODE(BoundImageFetch,                                     F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         )
 OPCODE(BoundImageQueryDimensions,                           U32x4,          U32,            U32,                                                            )
+OPCODE(BoundImageQueryLod,                                  F32x4,          U32,            Opaque,                                                         )
 
 OPCODE(ImageSampleImplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(ImageSampleExplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
@@ -398,6 +400,7 @@ OPCODE(ImageGather,                                         F32x4,          U32,
 OPCODE(ImageGatherDref,                                     F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
 OPCODE(ImageFetch,                                          F32x4,          U32,            Opaque,         Opaque,         Opaque,         Opaque,         )
 OPCODE(ImageQueryDimensions,                                U32x4,          U32,            U32,                                                            )
+OPCODE(ImageQueryLod,                                       F32x4,          U32,            Opaque,                                                         )
 
 // Warp operations
 OPCODE(VoteAll,                                             U1,             U1,                                                                             )
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 3b00d7c8c3..04e3a4f539 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -383,7 +383,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
     case IR::Opcode::ImageGather:
     case IR::Opcode::ImageGatherDref:
     case IR::Opcode::ImageFetch:
-    case IR::Opcode::ImageQueryDimensions: {
+    case IR::Opcode::ImageQueryDimensions:
+    case IR::Opcode::ImageQueryLod: {
         const TextureType type{inst.Flags<IR::TextureInstInfo>().type};
         info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D ||
                                 type == TextureType::Shadow1D || type == TextureType::ShadowArray1D;
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index dfacf848f4..6eb286b838 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -57,6 +57,9 @@ IR::Opcode IndexedInstruction(const IR::Inst& inst) {
     case IR::Opcode::BoundImageQueryDimensions:
     case IR::Opcode::BindlessImageQueryDimensions:
         return IR::Opcode::ImageQueryDimensions;
+    case IR::Opcode::BoundImageQueryLod:
+    case IR::Opcode::BindlessImageQueryLod:
+        return IR::Opcode::ImageQueryLod;
     default:
         return IR::Opcode::Void;
     }
@@ -72,6 +75,7 @@ bool IsBindless(const IR::Inst& inst) {
     case IR::Opcode::BindlessImageGatherDref:
     case IR::Opcode::BindlessImageFetch:
     case IR::Opcode::BindlessImageQueryDimensions:
+    case IR::Opcode::BindlessImageQueryLod:
         return true;
     case IR::Opcode::BoundImageSampleImplicitLod:
     case IR::Opcode::BoundImageSampleExplicitLod:
@@ -81,6 +85,7 @@ bool IsBindless(const IR::Inst& inst) {
     case IR::Opcode::BoundImageGatherDref:
     case IR::Opcode::BoundImageFetch:
     case IR::Opcode::BoundImageQueryDimensions:
+    case IR::Opcode::BoundImageQueryLod:
         return false;
     default:
         throw InvalidArgument("Invalid opcode {}", inst.Opcode());