commit e8ccd4fbcd8375d579042b5c38d8370ca271a00f (tree)
parent ffc510a70c3f1c3c28e1b2e444ccc826df121c05
Author: Ali Cheraghi <alichraghi@proton.me>
Date: Wed, 24 Jun 2026 20:52:21 +0330
spirv: allow specifying texel type for storage images
Diffstat:
5 files changed, 47 insertions(+), 53 deletions(-)
diff --git a/lib/std/lang.zig b/lib/std/lang.zig
@@ -838,7 +838,7 @@ pub const Type = union(enum) {
pub const Usage = union(enum(u2)) {
unknown: type,
sampled: type,
- storage,
+ storage: type,
};
pub const Format = enum(u4) {
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -20641,62 +20641,59 @@ fn zirReifySpirvType(
break :ip_data .{
.name = name,
.zir_index = tracked_inst,
- .ty = switch (usage_tag) {
- .sampled, .unknown => blk: {
- const sampled_type = usage_val.unionPayload(zcu).toType();
- std.hash.autoHash(&hasher, sampled_type.toIntern());
+ .ty = blk: {
+ const sampled_type = usage_val.unionPayload(zcu).toType();
+ std.hash.autoHash(&hasher, sampled_type.toIntern());
- if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
- return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
- }
- if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
- return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
- }
+ if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
+ return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
+ }
+ if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
+ return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
+ }
- if (sampled_type.toIntern() != .void_type and
- (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
- {
- return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
+ if (sampled_type.toIntern() != .void_type and
+ (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
+ {
+ return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
+ }
+
+ if (target.os.tag == .vulkan) {
+ const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
+ (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
+ if (!ok) {
+ return sema.fail(
+ block,
+ operand_src,
+ "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
+ .{usage_tag},
+ );
}
- if (target.os.tag == .vulkan) {
- const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
- (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
- if (!ok) {
+ if (format != .unknown) {
+ const format_kind: enum { float, sint, uint } = switch (format) {
+ .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
+ .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
+ .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
+ .unknown => unreachable,
+ };
+ const matches = switch (format_kind) {
+ .float => sampled_type.isRuntimeFloat(),
+ .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
+ .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
+ };
+ if (!matches) {
return sema.fail(
block,
operand_src,
- "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
- .{usage_tag},
+ "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
+ .{ format, usage_tag, sampled_type.fmt(pt) },
);
}
-
- if (format != .unknown) {
- const format_kind: enum { float, sint, uint } = switch (format) {
- .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
- .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
- .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
- .unknown => unreachable,
- };
- const matches = switch (format_kind) {
- .float => sampled_type.isRuntimeFloat(),
- .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
- .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
- };
- if (!matches) {
- return sema.fail(
- block,
- operand_src,
- "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
- .{ format, usage_tag, sampled_type.fmt(pt) },
- );
- }
- }
}
+ }
- break :blk sampled_type.toIntern();
- },
- .storage => .none,
+ break :blk sampled_type.toIntern();
},
.flags = .{
.tag = .image,
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
@@ -2345,10 +2345,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
switch (spirv_type.flags.tag) {
.sampler => try cg.sections.globals.emit(gpa, .OpTypeSampler, .{ .id_result = result_id }),
.image => {
- const sampled_type_id = if (spirv_type.ty == .none)
- try cg.intType(.unsigned, 32)
- else
- try cg.resolveType(Type.fromInterned(spirv_type.ty), .direct);
+ const sampled_type_id = try cg.resolveType(.fromInterned(spirv_type.ty), .direct);
try cg.sections.globals.emit(gpa, .OpTypeImage, .{
.id_result = result_id,
.sampled_type = sampled_type_id,
@@ -2366,7 +2363,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
.arrayed = @intFromBool(spirv_type.flags.is_arrayed),
.ms = @intFromBool(spirv_type.flags.is_multisampled),
.sampled = switch (spirv_type.flags.usage) {
- .unknown => 1,
+ .unknown => 0,
.sampled => 1,
.storage => 2,
},
diff --git a/test/behavior/spirv.zig b/test/behavior/spirv.zig
@@ -10,7 +10,7 @@ const Image = @SpirvType(.{ .image = .{
} });
const SampledImage = @SpirvType(.{ .sampled_image = Image });
const StorageImage = @SpirvType(.{ .image = .{
- .usage = .storage,
+ .usage = .{ .storage = u32 },
.format = .unknown,
.dim = .@"2d",
.depth = .unknown,
diff --git a/test/cases/compile_errors/SpirvType_vulkan_target.zig b/test/cases/compile_errors/SpirvType_vulkan_target.zig
@@ -1,6 +1,6 @@
comptime {
_ = @SpirvType(.{ .image = .{
- .usage = .storage,
+ .usage = .{ .storage = u32 },
.format = .unknown,
.dim = .@"2d",
.depth = .unknown,