zgpu

git clone git://git.electrosoup.com/zgpu
Log | Files | Refs | Submodules | README

commit ebcb28db0fb23a7e6e145e90d501a54205e4f4c6
parent 1c586261735d810e29d99a1070ea06d959077057
Author: Christian Ermann <christianermann@gmail.com>
Date:   Sun,  1 Jun 2025 15:50:18 -0700

Add shaders

Diffstat:
Asrc/bind_group_layout.zig | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/callback.zig | 11+++++++----
Msrc/common.zig | 24++++++++++++++++++++++++
Msrc/device.zig | 42+++++++++++++++++++++++++++++++++++++-----
Msrc/logging.zig | 2+-
Msrc/main.zig | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asrc/pipeline_layout.zig | 20++++++++++++++++++++
Msrc/render_pass.zig | 27+++++++++++++++++++++++++++
Asrc/render_pipeline.zig | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shader_module.zig | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/texture.zig | 10++++++++++
11 files changed, 608 insertions(+), 18 deletions(-)

diff --git a/src/bind_group_layout.zig b/src/bind_group_layout.zig @@ -0,0 +1,81 @@ +const c = @import("c.zig").c; +const ChainedStruct = @import("common.zig").ChainedStruct; +const StringView = @import("common.zig").StringView; +const Texture = @import("texture.zig").Texture; +const TextureView = @import("texture_view.zig").TextureView; + +pub const BindGroupLayout = opaque { + const Descriptor = extern struct { + next: ?*ChainedStruct = null, + label: StringView, + entry_count: usize, + entries: ?[*]const Entry, + }; + + const Entry = extern struct { + next: ?*ChainedStruct = null, + binding: u32, + visibility: ShaderStage, + buffer: BufferBindingLayout, + sampler: SamplerBindingLayout, + texture: TextureBindingLayout, + storage_texture: StorageTextureBindingLayout, + + const ShaderStage = packed struct(u64) { + vertex: bool = false, + fragment: bool = false, + compute: bool = false, + _padding: u61 = 0, + }; + + const BufferBindingLayout = extern struct { + next: ?*ChainedStruct = null, + type: BufferBindingType, + has_dynamic_offset: bool, + min_binding_size: u64, + + const BufferBindingType = enum(u32) { + binding_not_used = 0x00000000, + undefined = 0x00000001, + uniform = 0x00000002, + storage = 0x00000003, + read_only_storage = 0x00000004, + }; + }; + + const SamplerBindingLayout = extern struct { + next: ?*ChainedStruct = null, + type: SamplerBindingType, + + const SamplerBindingType = enum(u32) { + binding_not_used = 0x00000000, + undefined = 0x00000001, + filtering = 0x00000002, + non_filtering = 0x00000003, + comparison = 0x00000004, + }; + }; + + const TextureBindingLayout = extern struct { + next: ?*ChainedStruct = null, + sample_type: Texture.SampleType, + view_dimension: TextureView.Dimension, + multisampled: bool, + }; + + const StorageTextureBindingLayout = extern struct { + next: ?*ChainedStruct = null, + access: StorageTextureAccess, + format: Texture.Format, + view_dimension: TextureView.Dimension, + + const StorageTextureAccess = enum(u32) { + binding_not_used = 0x00000000, + undefined = 0x00000001, + write_only = 0x00000002, + read_only = 0x00000003, + read_write = 0x00000004, + }; + }; + }; +}; diff --git a/src/callback.zig b/src/callback.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Device = @import("device.zig").Device; const StringView = @import("common.zig").StringView; fn CallbackType1(comptime T: type) type { @@ -29,17 +30,17 @@ pub fn Callback1( }.wrapped_callback; } -fn CallbackType2(comptime T: type) type { +fn DeviceCallbackType(comptime T: type) type { const enum_type = switch (@typeInfo(T)) { .@"fn" => |f| f.params[0].type.?, else => unreachable, }; - return fn (enum_type, StringView, ?*anyopaque, ?*anyopaque) callconv(.C) void; + return fn (*const Device, enum_type, StringView, ?*anyopaque, ?*anyopaque) callconv(.C) void; } -pub fn Callback2( +pub fn DeviceCallback( user_callback: anytype, -) CallbackType2(@TypeOf(user_callback)) { +) DeviceCallbackType(@TypeOf(user_callback)) { const enum_type = switch (@typeInfo(@TypeOf(user_callback))) { .@"fn" => |f| f.params[0].type.?, else => @compileError("callback must be a function"), @@ -47,11 +48,13 @@ pub fn Callback2( return struct { const callback = user_callback; fn wrapped_callback( + device: *const Device, enum_value: enum_type, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque, ) callconv(.C) void { + _ = device; _ = userdata1; _ = userdata2; callback(enum_value, message.toSlice()); diff --git a/src/common.zig b/src/common.zig @@ -67,6 +67,12 @@ pub const StringView = extern struct { } }; +pub const OptionalBool = enum(u32) { + false = 0x00000000, + true = 0x00000001, + undefined = 0x00000002, +}; + pub const CallbackMode = enum(u32) { wait_any_only = 0x00000001, allow_process_events = 0x00000002, @@ -96,3 +102,21 @@ pub const Color = extern struct { b: f64, a: f64, }; + +pub const IndexFormat = enum(u32) { + undefined = 0x00000000, + uint16 = 0x00000001, + uint32 = 0x00000002, +}; + +pub const CompareFunction = enum(u32) { + undefined = 0x00000000, + never = 0x00000001, + less = 0x00000002, + equal = 0x00000003, + less_equal = 0x00000004, + greater = 0x00000005, + not_equal = 0x00000006, + greater_equal = 0x00000007, + always = 0x00000008, +}; diff --git a/src/device.zig b/src/device.zig @@ -4,7 +4,10 @@ const c = @import("c.zig").c; const CallbackMode = @import("common.zig").CallbackMode; const ChainedStruct = @import("common.zig").ChainedStruct; const CommandEncoder = @import("command_encoder.zig").CommandEncoder; +const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout; const Queue = @import("queue.zig").Queue; +const RenderPipeline = @import("render_pipeline.zig").RenderPipeline; +const ShaderModule = @import("shader_module.zig").ShaderModule; const StringView = @import("common.zig").StringView; pub const FeatureName = enum(u32) { @@ -74,6 +77,7 @@ pub const DeviceLostReason = enum(u32) { }; pub const DeviceLostCallback = fn ( + device: *const Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, @@ -96,7 +100,8 @@ pub const ErrorType = enum(u32) { unknown = 0x00000005, }; -pub const ErrorCallback = fn ( +pub const UncapturedErrorCallback = fn ( + device: *const Device, @"type": ErrorType, message: StringView, userdata1: ?*anyopaque, @@ -105,7 +110,7 @@ pub const ErrorCallback = fn ( pub const UncapturedErrorCallbackInfo = extern struct { next: ?*const ChainedStruct = null, - callback: ?*const ErrorCallback, + callback: ?*const UncapturedErrorCallback, userdata1: ?*anyopaque, userdata2: ?*anyopaque, }; @@ -140,16 +145,44 @@ pub const Device = opaque { )); } + pub fn createRenderPipeline( + device: *Device, + descriptor: *const RenderPipeline.Descriptor, + ) ?*RenderPipeline { + return @ptrCast(c.wgpuDeviceCreateRenderPipeline( + @ptrCast(device), + @ptrCast(descriptor), + )); + } + + pub fn createPipelineLayout( + device: *Device, + descriptor: *const PipelineLayout.Descriptor, + ) ?*PipelineLayout { + return @ptrCast(c.wgpuDeviceCreatePipelineLayout( + @ptrCast(device), + @ptrCast(descriptor), + )); + } + + pub fn createShaderModule( + device: *Device, + descriptor: *const ShaderModule.Descriptor, + ) ?*ShaderModule { + return @ptrCast(c.wgpuDeviceCreateShaderModule( + @ptrCast(device), + @ptrCast(descriptor), + )); + } + // createBindGroup // createBindGroupLayout // createBuffer - // createCommandEncoder // createComputePipeline // createComputePipelineAsync // createPipelineLayout // createQuerySet // createRenderBundleEncoder - // createRenderPipeline // createRenderPipelineAsync // createSampler // createShaderModule @@ -157,7 +190,6 @@ pub const Device = opaque { // destroy // enumerateFeatures // getLimits - // getQueue // hasFeature // popErrorScope // pushErrorScope diff --git a/src/logging.zig b/src/logging.zig @@ -5,7 +5,7 @@ const StringView = @import("common.zig").StringView; pub const LogLevel = enum(u32) { off = 0x00000000, - err = 0x00000001, + @"error" = 0x00000001, warn = 0x00000002, info = 0x00000003, debug = 0x00000004, diff --git a/src/main.zig b/src/main.zig @@ -1,17 +1,17 @@ const std = @import("std"); +const Callback1 = @import("callback.zig").Callback1; +const DeviceCallback = @import("callback.zig").DeviceCallback; +const CommandBuffer = @import("command_buffer.zig").CommandBuffer; +const Device = @import("device.zig"); const glfw = @import("glfw.zig"); const Instance = @import("instance.zig").Instance; const Logging = @import("logging.zig"); -const Device = @import("device.zig"); -const Callback1 = @import("callback.zig").Callback1; -const Callback2 = @import("callback.zig").Callback2; -const CommandBuffer = @import("command_buffer.zig").CommandBuffer; const StringView = @import("common.zig").StringView; -pub fn zgpu_log_callback(level: Logging.LogLevel, message: []const u8) void { +fn zgpu_log_callback(level: Logging.LogLevel, message: []const u8) void { switch (level) { - .err => { + .@"error" => { std.log.err("[wgpu] {s}", .{message}); }, .warn => { @@ -109,13 +109,13 @@ pub fn main() !void { .label = StringView.fromSlice("Example Default Queue"), }, .device_lost_callback_info = .{ - .callback = Callback2(zgpu_device_lost_callback), + .callback = DeviceCallback(zgpu_device_lost_callback), .mode = .allow_spontaneous, .userdata1 = null, .userdata2 = null, }, .uncaptured_error_callback_info = .{ - .callback = Callback2(zgpu_error_callback), + .callback = DeviceCallback(zgpu_error_callback), .userdata1 = null, .userdata2 = null, }, @@ -137,6 +137,105 @@ pub fn main() !void { .present_mode = .fifo, }); + const vs = + \\ @vertex fn main( + \\ @builtin(vertex_index) vtx_idx : u32 + \\ ) -> @builtin(position) vec4<f32> { + \\ let x = f32(i32(vtx_idx) - 1); + \\ let y = f32(i32(vtx_idx & 1u) * 2 - 1); + \\ return vec4<f32>(x, y, 0.0, 1.0); + \\ } + ; + const vs_module = device.createShaderModule(&.{ + .next = .{ .wgsl = &.{ .code = StringView.fromSlice(vs) } }, + .label = StringView.fromSlice("Example Vertex Shader"), + }) orelse { + std.log.err("failed to compile vertex shader", .{}); + std.process.exit(1); + }; + defer vs_module.release(); + + const fs = + \\ @fragment fn main() -> @location(0) vec4<f32> { + \\ return vec4<f32>(1.0, 0.0, 0.0, 1.0); + \\ } + ; + const fs_module = device.createShaderModule(&.{ + .next = .{ .wgsl = &.{ .code = StringView.fromSlice(fs) } }, + .label = StringView.fromSlice("Example Fragment Shader"), + }) orelse { + std.log.err("failed to compile fragment shader", .{}); + std.process.exit(1); + }; + defer fs_module.release(); + + const pipeline_layout = device.createPipelineLayout(&.{ + .label = StringView.fromSlice("Example Pipeline Layout"), + .bind_group_layout_count = 0, + .bind_group_layouts = null, + }).?; + defer pipeline_layout.release(); + + const pipeline = device.createRenderPipeline(&.{ + .label = StringView.fromSlice("Example Render Pipeline"), + .layout = pipeline_layout, + .vertex = .{ + .module = vs_module, + .entry_point = StringView.fromSlice("main"), + .constant_count = 0, + .constants = null, + .buffer_count = 0, + .buffers = null, + }, + .primitive = .{ + .topology = .triangle_list, + .strip_index_format = .undefined, + .front_face = .ccw, + .cull_mode = .none, + .unclipped_depth = false, + }, + .depth_stencil = null, + .multisample = .{ + .count = 1, + .mask = 0xFFFFFFFF, + .alpha_to_coverage_enabled = false, + }, + .fragment_state = &.{ + .module = fs_module, + .entry_point = StringView.fromSlice("main"), + .constant_count = 0, + .constants = null, + .target_count = 1, + .targets = &.{ + .{ + .format = .bgra8_unorm, + .blend = &.{ + .color = .{ + .operation = .add, + .src_factor = .one, + .dst_factor = .zero, + }, + .alpha = .{ + .operation = .add, + .src_factor = .one, + .dst_factor = .zero, + }, + }, + .write_mask = .{ + .red = true, + .green = true, + .blue = true, + .alpha = true, + }, + }, + }, + }, + }) orelse { + std.log.err("failed to create render pipeline", .{}); + std.process.exit(1); + }; + defer pipeline.release(); + while (!window.shouldClose()) { glfw.pollEvents(); const current_texture = surface.getCurrentTexture(); @@ -176,6 +275,10 @@ pub fn main() !void { .occlusion_query_set = null, .timestamp_writes = null, }); + + pass.setPipeline(pipeline); + pass.draw(3, 1, 0, 0); + defer pass.release(); defer pass.end(); } diff --git a/src/pipeline_layout.zig b/src/pipeline_layout.zig @@ -0,0 +1,20 @@ +const c = @import("c.zig").c; +const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout; +const ChainedStruct = @import("common.zig").ChainedStruct; +const StringView = @import("common.zig").StringView; + +pub const PipelineLayout = opaque { + pub const Descriptor = extern struct { + next: ?*ChainedStruct = null, + label: StringView, + bind_group_layout_count: u64, + bind_group_layouts: ?*const BindGroupLayout, + }; + + pub fn release(pipeline_layout: *PipelineLayout) void { + c.wgpuPipelineLayoutRelease(@ptrCast(pipeline_layout)); + } + + // setLabel + // reference +}; diff --git a/src/render_pass.zig b/src/render_pass.zig @@ -2,6 +2,7 @@ const c = @import("c.zig").c; const ChainedStruct = @import("common.zig").ChainedStruct; const Color = @import("common.zig").Color; const LoadOp = @import("common.zig").LoadOp; +const RenderPipeline = @import("render_pipeline.zig").RenderPipeline; const StoreOp = @import("common.zig").StoreOp; const StringView = @import("common.zig").StringView; const TextureView = @import("texture_view.zig").TextureView; @@ -59,4 +60,30 @@ pub const RenderPassEncoder = opaque { pub fn end(encoder: *RenderPassEncoder) void { c.wgpuRenderPassEncoderEnd(@ptrCast(encoder)); } + + pub fn setPipeline( + encoder: *RenderPassEncoder, + pipeline: *RenderPipeline, + ) void { + c.wgpuRenderPassEncoderSetPipeline( + @ptrCast(encoder), + @ptrCast(pipeline), + ); + } + + pub fn draw( + encoder: *RenderPassEncoder, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) void { + c.wgpuRenderPassEncoderDraw( + @ptrCast(encoder), + vertex_count, + instance_count, + first_vertex, + first_instance, + ); + } }; diff --git a/src/render_pipeline.zig b/src/render_pipeline.zig @@ -0,0 +1,247 @@ +const c = @import("c.zig").c; +const ChainedStruct = @import("common.zig").ChainedStruct; +const CompareFunction = @import("common.zig").CompareFunction; +const IndexFormat = @import("common.zig").IndexFormat; +const OptionalBool = @import("common.zig").OptionalBool; +const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout; +const ShaderModule = @import("shader_module.zig").ShaderModule; +const StringView = @import("common.zig").StringView; +const Texture = @import("texture.zig").Texture; + +const VertexState = extern struct { + next: ?*ChainedStruct = null, + module: *ShaderModule, + entry_point: StringView, + constant_count: usize, + constants: ?[*]const ConstantEntry, + buffer_count: usize, + buffers: ?[*]const VertexBufferLayout, + + const VertexBufferLayout = extern struct { + step_mode: VertexStepMode, + array_stride: u64, + attribute_count: usize, + attributes: ?[*]VertexAttribute, + + const VertexStepMode = enum(u32) { + vertex_buffer_not_used = 0x00000000, + undefined = 0x00000001, + vertex = 0x00000002, + instance = 0x00000003, + }; + + const VertexAttribute = extern struct { + format: VertexFormat, + offset: u64, + shader_location: u32, + }; + + const VertexFormat = enum(u32) { + uint8 = 0x00000001, + uint8x2 = 0x00000002, + uint8x4 = 0x00000003, + sint8 = 0x00000004, + sint8x2 = 0x00000005, + sint8x4 = 0x00000006, + unorm8 = 0x00000007, + unorm8x2 = 0x00000008, + unorm8x4 = 0x00000009, + snorm = 0x0000000A, + snorm8x2 = 0x0000000B, + snorm8x4 = 0x0000000C, + uint16 = 0x0000000D, + uint16x2 = 0x0000000E, + uint16x4 = 0x0000000F, + sint16 = 0x00000010, + sint16x2 = 0x00000011, + sint16x4 = 0x00000012, + unorm16 = 0x00000013, + unorm16x2 = 0x00000014, + unorm16x4 = 0x00000015, + snorm16 = 0x00000016, + snorm16x2 = 0x00000017, + snorm16x4 = 0x00000018, + float16 = 0x00000019, + float16x2 = 0x0000001A, + float16x4 = 0x0000001B, + float32 = 0x0000001C, + float32x2 = 0x0000001D, + float32x3 = 0x0000001E, + float32x4 = 0x0000001F, + uint32 = 0x00000020, + uint32x2 = 0x00000021, + uint32x3 = 0x00000022, + uint32x4 = 0x00000023, + sint32 = 0x00000024, + sint32x2 = 0x00000025, + sint32x3 = 0x00000026, + sint32x4 = 0x00000027, + unorm10_10_10_2 = 0x00000028, + unorm8x4_brga = 0x00000029, + }; + }; +}; + +const FragmentState = extern struct { + next: ?*ChainedStruct = null, + module: *ShaderModule, + entry_point: StringView, + constant_count: usize, + constants: ?[*]const ConstantEntry, + target_count: usize, + targets: ?[*]const ColorTargetState, + + const ColorTargetState = extern struct { + next: ?*ChainedStruct = null, + format: Texture.Format, + blend: ?*const BlendState, + write_mask: ColorWriteMask, + + const BlendState = extern struct { + color: BlendComponent, + alpha: BlendComponent, + }; + + const BlendComponent = extern struct { + operation: BlendOperation, + src_factor: BlendFactor, + dst_factor: BlendFactor, + }; + + const BlendOperation = enum(u32) { + undefined = 0x00000000, + add = 0x00000001, + subtract = 0x00000002, + reverse_subtract = 0x00000003, + min = 0x00000004, + max = 0x00000005, + }; + + const BlendFactor = enum(u32) { + undefined = 0x00000000, + zero = 0x00000001, + one = 0x00000002, + src = 0x00000003, + one_minus_src = 0x00000004, + src_alpha = 0x00000005, + one_minus_src_alpha = 0x00000006, + dst = 0x00000007, + one_minus_dst = 0x00000008, + dst_alpha = 0x00000009, + one_minus_dst_alpha = 0x0000000A, + src_alpha_saturated = 0x0000000B, + constant = 0x0000000C, + one_minus_constant = 0x0000000D, + src1 = 0x0000000E, + one_minus_src1 = 0x0000000F, + src1_alpha = 0x00000010, + one_minus_src1_alpha = 0x00000011, + }; + + const ColorWriteMask = packed struct(u64) { + red: bool = false, + green: bool = false, + blue: bool = false, + alpha: bool = false, + _padding: u60 = 0, + }; + }; +}; + +const ConstantEntry = extern struct { + next: ?*ChainedStruct = null, + key: StringView, + value: f64, +}; + +const PrimitiveState = extern struct { + next: ?*ChainedStruct = null, + topology: PrimitiveTopology, + strip_index_format: IndexFormat, + front_face: FrontFace, + cull_mode: CullMode, + unclipped_depth: bool, + + const PrimitiveTopology = enum(u32) { + undefined = 0x00000000, + point_list = 0x00000001, + line_list = 0x00000002, + line_strip = 0x00000003, + triangle_list = 0x00000004, + triangle_strip = 0x00000005, + }; + + const FrontFace = enum(u32) { + undefined = 0x00000000, + ccw = 0x00000001, + cw = 0x00000002, + }; + + const CullMode = enum(u32) { + undefined = 0x00000000, + none = 0x00000001, + front = 0x00000002, + back = 0x00000003, + }; +}; + +const DepthStencilState = extern struct { + next: ?*ChainedStruct = null, + format: Texture.Format, + depth_write_enabled: OptionalBool, + depth_compare: CompareFunction, + stencil_front: StencilFaceState, + stencil_back: StencilFaceState, + stencil_read_mask: u32, + stencil_write_mask: u32, + depth_bias: i32, + depth_bias_slope_scale: f32, + depth_bias_clamp: f32, + + const StencilFaceState = extern struct { + compare: CompareFunction, + fail_op: StencilOperation, + depth_fail_op: StencilOperation, + pass_op: StencilOperation, + }; + + const StencilOperation = enum(u32) { + undefined = 0x00000000, + keep = 0x00000001, + zero = 0x00000002, + replace = 0x00000003, + invert = 0x00000004, + increment_clamp = 0x00000005, + decrement_clamp = 0x00000006, + increment_wrap = 0x00000007, + decrement_wrap = 0x00000008, + }; +}; + +const MultisampleState = extern struct { + next: ?*ChainedStruct = null, + count: u32, + mask: u32, + alpha_to_coverage_enabled: bool, +}; + +pub const RenderPipeline = opaque { + pub const Descriptor = extern struct { + next: ?*ChainedStruct = null, + label: StringView, + layout: ?*const PipelineLayout, + vertex: VertexState, + primitive: PrimitiveState, + depth_stencil: ?*const DepthStencilState, + multisample: MultisampleState, + fragment_state: ?*const FragmentState, + }; + + pub fn release(pipeline: *RenderPipeline) void { + c.wgpuRenderPipelineRelease(@ptrCast(pipeline)); + } + + // getBindGroupLayout + // setLabel + // reference +}; diff --git a/src/shader_module.zig b/src/shader_module.zig @@ -0,0 +1,43 @@ +const c = @import("c.zig").c; +const ChainedStruct = @import("common.zig").ChainedStruct; +const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout; +const StringView = @import("common.zig").StringView; + +pub const ShaderModule = opaque { + pub const Descriptor = extern struct { + next: Next = .{ .generic = null }, + label: StringView, + + pub const Next = extern union { + generic: ?*const ChainedStruct, + spirv: *const SourceSPIRV, + wgsl: *const SourceWGSL, + }; + }; + + const SourceSPIRV = extern struct { + chain: ChainedStruct = .{ + .next = null, + .struct_type = .shader_source_spirv, + }, + code_size: u32, + code: ?[*]const u32, + }; + + const SourceWGSL = extern struct { + chain: ChainedStruct = .{ + .next = null, + .struct_type = .shader_source_wgsl, + }, + code: StringView, + }; + + pub fn release(module: *ShaderModule) void { + c.wgpuShaderModuleRelease(@ptrCast(module)); + } + + // getCompilationInfo + // setLabel + // reference + // release +}; diff --git a/src/texture.zig b/src/texture.zig @@ -116,6 +116,16 @@ pub const Texture = opaque { _padding: u59 = 0, }; + pub const SampleType = enum(u32) { + binding_not_used = 0x00000000, + undefined = 0x00000001, + float = 0x00000002, + unfilterable_float = 0x00000003, + depth = 0x00000004, + sint = 0x00000005, + uint = 0x00000006, + }; + pub fn createView( texture: *Texture, descriptor: *const TextureView.Descriptor,