authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-06-24 20:52:21+03:30
committergravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-06-25 15:47:10+02:00
loge8ccd4fbcd8375d579042b5c38d8370ca271a00f
treef94d3156f4f2abf5770dc4c684d40247d9b4c793
parentffc510a70c3f1c3c28e1b2e444ccc826df121c05

spirv: allow specifying texel type for storage images


5 files changed, 47 insertions(+), 53 deletions(-)

lib/std/lang.zig+1-1
......@@ -838,7 +838,7 @@ pub const Type = union(enum) {
838838 pub const Usage = union(enum(u2)) {
839839 unknown: type,
840840 sampled: type,
841 storage,
841 storage: type,
842842 };
843843
844844 pub const Format = enum(u4) {
src/Sema.zig+42-45
......@@ -20641,62 +20641,59 @@ fn zirReifySpirvType(
2064120641 break :ip_data .{
2064220642 .name = name,
2064320643 .zir_index = tracked_inst,
20644 .ty = switch (usage_tag) {
20645 .sampled, .unknown => blk: {
20646 const sampled_type = usage_val.unionPayload(zcu).toType();
20647 std.hash.autoHash(&hasher, sampled_type.toIntern());
20644 .ty = blk: {
20645 const sampled_type = usage_val.unionPayload(zcu).toType();
20646 std.hash.autoHash(&hasher, sampled_type.toIntern());
2064820647
20649 if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
20650 return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
20651 }
20652 if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
20653 return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
20654 }
20648 if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
20649 return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
20650 }
20651 if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
20652 return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
20653 }
2065520654
20656 if (sampled_type.toIntern() != .void_type and
20657 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
20658 {
20659 return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
20655 if (sampled_type.toIntern() != .void_type and
20656 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
20657 {
20658 return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
20659 }
20660
20661 if (target.os.tag == .vulkan) {
20662 const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
20663 (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
20664 if (!ok) {
20665 return sema.fail(
20666 block,
20667 operand_src,
20668 "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
20669 .{usage_tag},
20670 );
2066020671 }
2066120672
20662 if (target.os.tag == .vulkan) {
20663 const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
20664 (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
20665 if (!ok) {
20673 if (format != .unknown) {
20674 const format_kind: enum { float, sint, uint } = switch (format) {
20675 .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
20676 .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
20677 .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
20678 .unknown => unreachable,
20679 };
20680 const matches = switch (format_kind) {
20681 .float => sampled_type.isRuntimeFloat(),
20682 .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
20683 .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
20684 };
20685 if (!matches) {
2066620686 return sema.fail(
2066720687 block,
2066820688 operand_src,
20669 "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
20670 .{usage_tag},
20689 "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
20690 .{ format, usage_tag, sampled_type.fmt(pt) },
2067120691 );
2067220692 }
20673
20674 if (format != .unknown) {
20675 const format_kind: enum { float, sint, uint } = switch (format) {
20676 .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
20677 .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
20678 .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
20679 .unknown => unreachable,
20680 };
20681 const matches = switch (format_kind) {
20682 .float => sampled_type.isRuntimeFloat(),
20683 .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
20684 .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
20685 };
20686 if (!matches) {
20687 return sema.fail(
20688 block,
20689 operand_src,
20690 "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
20691 .{ format, usage_tag, sampled_type.fmt(pt) },
20692 );
20693 }
20694 }
2069520693 }
20694 }
2069620695
20697 break :blk sampled_type.toIntern();
20698 },
20699 .storage => .none,
20696 break :blk sampled_type.toIntern();
2070020697 },
2070120698 .flags = .{
2070220699 .tag = .image,
src/codegen/spirv/CodeGen.zig+2-5
......@@ -2345,10 +2345,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
23452345 switch (spirv_type.flags.tag) {
23462346 .sampler => try cg.sections.globals.emit(gpa, .OpTypeSampler, .{ .id_result = result_id }),
23472347 .image => {
2348 const sampled_type_id = if (spirv_type.ty == .none)
2349 try cg.intType(.unsigned, 32)
2350 else
2351 try cg.resolveType(Type.fromInterned(spirv_type.ty), .direct);
2348 const sampled_type_id = try cg.resolveType(.fromInterned(spirv_type.ty), .direct);
23522349 try cg.sections.globals.emit(gpa, .OpTypeImage, .{
23532350 .id_result = result_id,
23542351 .sampled_type = sampled_type_id,
......@@ -2366,7 +2363,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
23662363 .arrayed = @intFromBool(spirv_type.flags.is_arrayed),
23672364 .ms = @intFromBool(spirv_type.flags.is_multisampled),
23682365 .sampled = switch (spirv_type.flags.usage) {
2369 .unknown => 1,
2366 .unknown => 0,
23702367 .sampled => 1,
23712368 .storage => 2,
23722369 },
test/behavior/spirv.zig+1-1
......@@ -10,7 +10,7 @@ const Image = @SpirvType(.{ .image = .{
1010} });
1111const SampledImage = @SpirvType(.{ .sampled_image = Image });
1212const StorageImage = @SpirvType(.{ .image = .{
13 .usage = .storage,
13 .usage = .{ .storage = u32 },
1414 .format = .unknown,
1515 .dim = .@"2d",
1616 .depth = .unknown,
test/cases/compile_errors/SpirvType_vulkan_target.zig+1-1
......@@ -1,6 +1,6 @@
11comptime {
22 _ = @SpirvType(.{ .image = .{
3 .usage = .storage,
3 .usage = .{ .storage = u32 },
44 .format = .unknown,
55 .dim = .@"2d",
66 .depth = .unknown,