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) {...@@ -838,7 +838,7 @@ pub const Type = union(enum) {
838 pub const Usage = union(enum(u2)) {838 pub const Usage = union(enum(u2)) {
839 unknown: type,839 unknown: type,
840 sampled: type,840 sampled: type,
841 storage,841 storage: type,
842 };842 };
843843
844 pub const Format = enum(u4) {844 pub const Format = enum(u4) {
src/Sema.zig+42-45
...@@ -20641,62 +20641,59 @@ fn zirReifySpirvType(...@@ -20641,62 +20641,59 @@ fn zirReifySpirvType(
20641 break :ip_data .{20641 break :ip_data .{
20642 .name = name,20642 .name = name,
20643 .zir_index = tracked_inst,20643 .zir_index = tracked_inst,
20644 .ty = switch (usage_tag) {20644 .ty = blk: {
20645 .sampled, .unknown => blk: {20645 const sampled_type = usage_val.unionPayload(zcu).toType();
20646 const sampled_type = usage_val.unionPayload(zcu).toType();20646 std.hash.autoHash(&hasher, sampled_type.toIntern());
20647 std.hash.autoHash(&hasher, sampled_type.toIntern());
2064820647
20649 if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {20648 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});20649 return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
20651 }20650 }
20652 if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {20651 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});20652 return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
20654 }20653 }
2065520654
20656 if (sampled_type.toIntern() != .void_type and20655 if (sampled_type.toIntern() != .void_type and
20657 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))20656 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
20658 {20657 {
20659 return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });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 );
20660 }20671 }
2066120672
20662 if (target.os.tag == .vulkan) {20673 if (format != .unknown) {
20663 const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or20674 const format_kind: enum { float, sint, uint } = switch (format) {
20664 (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));20675 .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
20665 if (!ok) {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) {
20666 return sema.fail(20686 return sema.fail(
20667 block,20687 block,
20668 operand_src,20688 operand_src,
20669 "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",20689 "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
20670 .{usage_tag},20690 .{ format, usage_tag, sampled_type.fmt(pt) },
20671 );20691 );
20672 }20692 }
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 }
20695 }20693 }
20694 }
2069620695
20697 break :blk sampled_type.toIntern();20696 break :blk sampled_type.toIntern();
20698 },
20699 .storage => .none,
20700 },20697 },
20701 .flags = .{20698 .flags = .{
20702 .tag = .image,20699 .tag = .image,
src/codegen/spirv/CodeGen.zig+2-5
...@@ -2345,10 +2345,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2345,10 +2345,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2345 switch (spirv_type.flags.tag) {2345 switch (spirv_type.flags.tag) {
2346 .sampler => try cg.sections.globals.emit(gpa, .OpTypeSampler, .{ .id_result = result_id }),2346 .sampler => try cg.sections.globals.emit(gpa, .OpTypeSampler, .{ .id_result = result_id }),
2347 .image => {2347 .image => {
2348 const sampled_type_id = if (spirv_type.ty == .none)2348 const sampled_type_id = try cg.resolveType(.fromInterned(spirv_type.ty), .direct);
2349 try cg.intType(.unsigned, 32)
2350 else
2351 try cg.resolveType(Type.fromInterned(spirv_type.ty), .direct);
2352 try cg.sections.globals.emit(gpa, .OpTypeImage, .{2349 try cg.sections.globals.emit(gpa, .OpTypeImage, .{
2353 .id_result = result_id,2350 .id_result = result_id,
2354 .sampled_type = sampled_type_id,2351 .sampled_type = sampled_type_id,
...@@ -2366,7 +2363,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2366,7 +2363,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2366 .arrayed = @intFromBool(spirv_type.flags.is_arrayed),2363 .arrayed = @intFromBool(spirv_type.flags.is_arrayed),
2367 .ms = @intFromBool(spirv_type.flags.is_multisampled),2364 .ms = @intFromBool(spirv_type.flags.is_multisampled),
2368 .sampled = switch (spirv_type.flags.usage) {2365 .sampled = switch (spirv_type.flags.usage) {
2369 .unknown => 1,2366 .unknown => 0,
2370 .sampled => 1,2367 .sampled => 1,
2371 .storage => 2,2368 .storage => 2,
2372 },2369 },
test/behavior/spirv.zig+1-1
...@@ -10,7 +10,7 @@ const Image = @SpirvType(.{ .image = .{...@@ -10,7 +10,7 @@ const Image = @SpirvType(.{ .image = .{
10} });10} });
11const SampledImage = @SpirvType(.{ .sampled_image = Image });11const SampledImage = @SpirvType(.{ .sampled_image = Image });
12const StorageImage = @SpirvType(.{ .image = .{12const StorageImage = @SpirvType(.{ .image = .{
13 .usage = .storage,13 .usage = .{ .storage = u32 },
14 .format = .unknown,14 .format = .unknown,
15 .dim = .@"2d",15 .dim = .@"2d",
16 .depth = .unknown,16 .depth = .unknown,
test/cases/compile_errors/SpirvType_vulkan_target.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1comptime {1comptime {
2 _ = @SpirvType(.{ .image = .{2 _ = @SpirvType(.{ .image = .{
3 .usage = .storage,3 .usage = .{ .storage = u32 },
4 .format = .unknown,4 .format = .unknown,
5 .dim = .@"2d",5 .dim = .@"2d",
6 .depth = .unknown,6 .depth = .unknown,