more features, and compiles (no shaders tho)

This commit is contained in:
Vivianne 2022-07-19 22:41:34 -07:00
parent fbaa4846b6
commit 586a3b256a
10 changed files with 435 additions and 43 deletions

View file

@ -6,12 +6,70 @@ const Device = @import("device.zig").Device;
const ProfileMark = @import("../../common/profiler.zig").ProfileMark;
const settings = @import("settings.zig");
const Swapchain = @import("swapchain.zig").Swapchain;
const descriptor = @import("descriptor.zig");
var s_layout = vk.DescriptorSetLayout.null_handle;
var s_pool = vk.DescriptorPool.null_handle;
var s_sets = std.mem.zeroes([settings.resource_sets]vk.DescriptorSet);
var s_writes = std.mem.zeroes([settings.Bid.Count]vk.WriteDescriptorSet);
var s_bindings = std.mem.zeroes([settings.Bid.Count]Binding);
var s_dirty = std.mem.zeroes([settings.Bid.Count]i32);
const bindings = [_]settings.Bid{
settings.Bid.TextureTable.T1D,
settings.Bid.TextureTable.T2D,
settings.Bid.TextureTable.T3D,
settings.Bid.TextureTable.TCube,
settings.Bid.TextureTable.T2DArray,
settings.Bid.Globals,
settings.Bid.SceneLuminance,
settings.Bid.RWSceneLuminance,
settings.Bid.HistogramBuffer,
settings.Bid.ExposureBuffer,
};
pub const set_layout_bindings = LayoutBindings();
fn LayoutBindings() [bindings.len]vk.DescriptorSetLayoutBinding {
assert(bindings.len == settings.Bid.Count);
inline for (set_layout_bindings) |*b, i| {
b.* = .{
.binding = bindings[i].id,
.descriptor_type = bindings[i].desc_type,
.descriptor_count = bindings[i].count,
.stage_flags = bindings[i].stages,
};
}
}
const pm_init = ProfileMark.init("Bindings.init");
pub fn init(device: *const Device) !void {
pm_init.begin();
defer pm_init.end();
errdefer deinit(device);
s_layout = try descriptor.Set.Layout.init(device, set_layout_bindings);
s_pool = try descriptor.Pool.init(device, descriptor.Pool.sizes);
for (s_sets) |*set| {
set.* = try descriptor.Set.init(device, s_pool, s_layout);
}
}
pub fn deinit(device: *const Device) void {
if (s_pool != .null_handle) {
for (s_sets) |*set| {
descriptor.Set.deinit(device, s_pool, set);
set.* = .null_handle;
}
descriptor.Set.Layout.deinit(device, s_pool);
s_pool = .null_handle;
}
descriptor.Set.Layout.deinit(device, s_layout);
s_layout = .null_handle;
}
const pm_update = ProfileMark.init("Bindings.update");
pub fn update(device: *const Device, swapchain: *const Swapchain) void {
pm_update.begin();
@ -32,6 +90,11 @@ pub fn getSet(swapchain: *const Swapchain) vk.DescriptorSet {
return s_sets[sync_index];
}
pub fn getSetLayout() vk.DescriptorSetLayout {
assert(s_layout != .null_handle);
return s_layout;
}
const pm_flush = ProfileMark.init("Bindings.flush");
pub fn flush(device: *const Device, swapchain: *const Swapchain) void {
pm_flush.begin();

View file

@ -224,9 +224,9 @@ pub const Buffer = struct {
assert(self.handle != .null_handle);
assert(self.gfx or self.comp);
assert(pass.pipeline != .null_handle);
device.dispatch.cmdBindPipeline(self.handle, pass.bindpoint, pass.pipeline);
device.dispatch.cmdBindPipeline(self.handle, pass.bind_point, pass.pipeline);
const set = Bindings.getSet(swapchain);
self.bindDescSets(device, pass.bindpoint, pass.layout, 1, @ptrCast([*]const vk.DescriptorSet, &set));
self.bindDescSets(device, pass.bind_point, pass.layout, 1, @ptrCast([*]const vk.DescriptorSet, &set));
}
const pm_buffer_beginrenderpass = ProfileMark.init("Command.Buffer.beginRenderPass");

View file

@ -22,7 +22,7 @@ const PushConstants = struct {
pub fn init(device: *const Device, swapchain: *Swapchain) !void {
errdefer {
std.debug.print("Failed to init MainPass", .{});
deinit();
deinit(device);
}
// try ScreenBlit.init()
@ -32,9 +32,9 @@ pub fn init(device: *const Device, swapchain: *Swapchain) !void {
// try UIPass.init();
}
pub fn deinit() void {
pub fn deinit(device: *const Device) void {
// ScreenBlit.deinit()
DepthPass.deinit();
DepthPass.deinit(device);
OpaquePass.deinit();
// Exposure.deinit();
// UIPass.deinit();
@ -69,7 +69,7 @@ const DepthPass = struct {
var s_pass: Pass = undefined;
pub fn init(device: *const Device, swapchain: *Swapchain) !void {
errdefer DepthPass.deinit();
errdefer DepthPass.deinit(device);
const depth_buffer = Targets.getDepthBuffer(swapchain);
@ -114,7 +114,7 @@ const DepthPass = struct {
.push_constant_bytes = @sizeOf(PushConstants),
.shaders = &shaders,
.render_pass = s_render_pass,
.subpass = -1,
.subpass = null,
.vert_layout = .{
.bindings = &vert_bindings,
.attributes = &vert_attributes,
@ -134,11 +134,11 @@ const DepthPass = struct {
.attachment_count = 0,
},
};
try Pass.init(&s_pass, &createInfo);
try s_pass.init(device, &createInfo);
}
pub fn deinit() void {
s_pass.deinit();
pub fn deinit(device: *const Device) void {
s_pass.deinit(device);
}
pub fn setup() void {}

View file

@ -1,19 +1,25 @@
const std = @import("std");
const assert = std.debug.assert;
const vk = @import("vulkan");
const Device = @import("device.zig").Device;
const Bindings = @import("Bindings.zig");
const pipeline = @import("pipeline.zig");
const Self = @This();
pipeline: vk.Pipeline,
layout: vk.PipelineLayout,
bindpoint: vk.PipelineBindPoint,
bind_point: vk.PipelineBindPoint,
stage_flags: vk.ShaderStageFlags,
push_constant_bytes: i32,
push_constant_bytes: u32,
const VertexLayout = struct {
pub const VertexLayout = struct {
bindings: []vk.VertexInputBindingDescription,
attributes: []vk.VertexInputAttributeDescription,
};
const BlendState = struct {
pub const BlendState = struct {
color_wite_mask: vk.ColorComponentFlags,
blend_enable: bool,
src_color_blend_factor: vk.BlendFactor,
@ -24,7 +30,7 @@ const BlendState = struct {
alpha_blend_op: vk.BlendOp,
};
const FixedFuncs = struct {
pub const FixedFuncs = struct {
viewport: vk.Viewport,
scissor: vk.Rect2D,
topology: vk.PrimitiveTopology,
@ -42,20 +48,76 @@ const FixedFuncs = struct {
pub const Description = struct {
// Graphics and compute
push_constant_bytes: i32,
push_constant_bytes: u32,
shaders: []vk.PipelineShaderStageCreateInfo,
// Graphics only
render_pass: vk.RenderPass,
subpass: i32,
subpass: ?u32,
vert_layout: VertexLayout,
fixed_funcs: FixedFuncs,
};
pub fn init(self: *Self, desc: *const Description) !void {
_ = self;
_ = desc;
pub fn init(self: *Self, device: *const Device, desc: *const Description) !void {
self.* = std.mem.zeroes(Self);
var stage_flags = vk.ShaderStageFlags{};
for (desc.shaders) |shader| {
stage_flags = stage_flags.merge(shader.stage);
}
var bind_point = vk.PipelineBindPoint.graphics;
if (stage_flags.compute_bit) {
bind_point = .compute;
}
const k_rt_stages = vk.ShaderStageFlags{
.raygen_bit_khr = true,
.any_hit_bit_khr = true,
.closest_hit_bit_khr = true,
.miss_bit_khr = true,
.intersection_bit_khr = true,
.callable_bit_khr = true,
};
if (stage_flags.intersect(k_rt_stages).toInt() != 0) {
bind_point = .ray_tracing_khr;
}
const set_layouts = [_]vk.DescriptorSetLayout{
Bindings.getSetLayout(),
};
const ranges = [_]vk.PushConstantRange{
.{
.stage_flags = stage_flags,
.size = desc.push_constant_bytes,
.offset = 0,
},
};
self.bind_point = bind_point;
self.stage_flags = stage_flags;
self.push_constant_bytes = desc.push_constant_bytes;
assert(desc.push_constant_bytes <= 256);
errdefer self.deinit(device);
self.layout = try pipeline.Layout.init(device, &set_layouts, &ranges);
switch (bind_point) {
.compute => {
assert(desc.shaders.len == 1);
self.pipeline = try pipeline.newComp(device, self.layout, &desc.shaders[0]);
},
.graphics => {
assert(desc.shaders.len == 2);
self.pipeline = try pipeline.newGfx(device, &desc.fixed_funcs, &desc.vert_layout, self.layout, desc.render_pass, desc.subpass, desc.shaders);
},
else => assert(false),
}
}
pub fn deinit(self: *Self) void {
_ = self;
pub fn deinit(self: *Self, device: *const Device) void {
pipeline.Layout.deinit(device, self.layout);
pipeline.deinit(device, self.pipeline);
self.* = std.mem.zeroes(Self);
}

View file

@ -110,7 +110,7 @@ pub fn deinit(self: Self) void {
// TODO: delete lightmap pack
MainPass.deinit();
MainPass.deinit(&self.device);
// self.imSys.deinit();
// self.meshSys.deinit();

View file

@ -10,3 +10,7 @@ pub fn init(info: *const vk.PipelineShaderStageCreateInfo, file_path: []const u8
_ = section;
_ = thingie;
}
pub fn deinit(shader: *const vk.PipelineShaderStageCreateInfo) void {
_ = shader;
}

View file

@ -0,0 +1,100 @@
const assert = @import("std").debug.assert;
const vk = @import("vulkan");
const ProfileMark = @import("../../common/profiler.zig").ProfileMark;
const Device = @import("device.zig").Device;
const pm_writeimagetable = ProfileMark.init("descriptor.writeImageTable");
pub fn writeImageTable(device: *const Device, set: vk.DescriptorSet, binding: i32, desc_type: vk.DescriptorType, bindings: []const vk.DescriptorImageInfo) void {
pm_writeimagetable.begin();
defer pm_writeimagetable.end();
device.dispatch.updateDescriptorSets(device.handle, 1, @ptrCast([*]const vk.WriteDescriptorSet, &.{
.descriptor_type = desc_type,
.dst_set = set,
.dst_binding = binding,
.dst_array_element = 0,
.descriptor_count = bindings.len,
.p_image_info = bindings.ptr,
}), 0, null);
}
const pm_writebuffertable = ProfileMark.init("descriptor.writeBufferTable");
pub fn writeBufferTable(device: *const Device, set: vk.DescriptorSet, binding: i32, desc_type: vk.DescriptorType, bindings: []const vk.DescriptorBufferInfo) void {
assert(set != .null_handle);
assert(@enumToInt(desc_type) >= @enumToInt(vk.DescriptorType.uniform_texel_buffer));
assert(@enumToInt(desc_type) <= @enumToInt(vk.DescriptorType.storage_buffer_dynamic));
pm_writebuffertable.begin();
defer pm_writebuffertable.end();
device.dispatch.updateDescriptorSets(device.handle, 1, @ptrCast([*]vk.WriteDescriptorSet, &.{
.descriptor_type = type,
.dst_set = set,
.dst_binding = binding,
.dst_array_element = 0,
.descriptor_count = bindings.len,
.p_buffer_info = bindings.ptr,
}), 0, null);
}
pub const Set = struct {
pub fn init(device: *const Device, pool: vk.DescriptorPool, layout: vk.DescriptorSetLayout) !void {
assert(pool != .null_handle);
assert(layout != .null_handle);
return try device.dispatch.allocateDescriptorSets(device.handle, &.{
.descriptor_pool = pool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast([*]const vk.DescriptorSetLayout, &layout),
});
}
pub fn deinit(device: *const Device, pool: vk.DescriptorPool, set: vk.DescriptorSet) void {
assert(pool != .null_handle);
if (set != .null_handle) {
try device.dispatch.freeDescriptorSets(device.handle, pool, 1, @ptrCast([*]const vk.DescriptorSet, &set));
}
}
pub const Layout = struct {
pub fn init(device: *const Device, bindings: []const vk.DescriptorSetLayoutBinding, flags: vk.DescriptorSetLayoutCreateFlags) !vk.DescriptorSetLayout {
return try device.dispatch.createDescriptorSetLayout(device.handle, &.{
.flags = flags,
.binding_count = bindings.len,
.p_bindings = bindings.ptr,
}, null);
}
pub fn deinit(device: *const Device, handle: vk.DescriptorSetLayout) void {
if (handle != .null_handle) {
return device.destroyDescriptorSetLayout(device.handle, handle, null);
}
}
};
};
pub const Pool = struct {
pub fn init(device: *const Device, max_sets: i32, sizes: []const vk.DescriptorPoolSize) !vk.DescriptorPool {
assert(max_sets > 0);
return try device.dispatch.createDescriptorPool(device.hande, &.{
.flags = .{ .create_free_descriptor_set_bit = true },
.max_sets = max_sets,
.pool_size_count = sizes.len,
.p_pool_sizes = sizes.ptr,
}, null);
}
pub fn deinit(device: *const Device, pool: vk.DescriptorPool) void {
if (pool != .null_handle) {
device.dispatch.destroyDescriptorPool(device.handle, pool, null);
}
}
const pm_pool_reset = ProfileMark.init("descriptor.Pool.reset");
pub fn reset(device: *const Device, pool: vk.DescriptorPool) !void {
pm_pool_reset.begin();
defer pm_pool_reset.end();
assert(pool != .null_handle);
try device.dispatch.resetDescriptorPool(device.handle, pool, .{});
}
};

View file

@ -49,6 +49,7 @@ pub const DeviceDispatch = vk.DeviceWrapper(.{
.createRenderPass = true,
.destroyRenderPass = true,
.createGraphicsPipelines = true,
.createComputePipelines = true,
.destroyPipeline = true,
.createFramebuffer = true,
.destroyFramebuffer = true,

View file

@ -2,17 +2,33 @@ const std = @import("std");
const assert = std.debug.assert;
const vk = @import("vulkan");
const vec = @import("vec");
const Vec2f = vec.Vec2f;
const Vec3f = vec.Vec3f;
const Vec4f = vec.Vec4f;
const Vec2i = vec.Vec2i;
const Vec3i = vec.Vec3i;
const Vec4i = vec.Vec4i;
const Device = @import("device.zig").Device;
const FixedFuncs = @import("Pass.zig").FixedFuncs;
const VertexLayout = @import("Pass.zig").VertexLayout;
pub const Layout = struct {
pub fn init(device: *const Device, set_layouts: []*const vk.DescriptorSetLayout, ranges: []*const vk.PushConstantRange) !vk.PipelineLayout {
pub fn init(device: *const Device, set_layouts: []const vk.DescriptorSetLayout, ranges: []const vk.PushConstantRange) !vk.PipelineLayout {
return try device.dispatch.createPipelineLayout(device.handle, &.{
.set_layout_count = set_layouts.len,
.flags = .{},
.set_layout_count = @intCast(u32, set_layouts.len),
.p_set_layouts = set_layouts.ptr,
.push_constant_range_count = ranges.len,
.push_constant_range_count = @intCast(u32, ranges.len),
.p_push_constant_ranges = ranges.ptr,
}, null);
}
pub fn deinit(device: *const Device, layout: vk.PipelineLayout) void {
device.dispatch.destroyPipelineLayout(device.handle, layout, null);
}
};
pub const VertType = enum {
@ -28,13 +44,13 @@ pub const VertType = enum {
pub fn size(t: VertType) i32 {
return switch (t) {
.float => @sizeOf(f32),
.float2 => @sizeOf(Float2),
.float3 => @sizeOf(Float3),
.float4 => @sizeOf(Float4),
.float2 => @sizeOf(Vec2f),
.float3 => @sizeOf(Vec3f),
.float4 => @sizeOf(Vec4f),
.int => @sizeOf(i32),
.int2 => @sizeOf(Int2),
.int3 => @sizeOf(Int3),
.int4 => @sizeOf(Int4),
.int2 => @sizeOf(Vec2i),
.int3 => @sizeOf(Vec3i),
.int4 => @sizeOf(Vec4i),
else => assert(false),
};
}
@ -53,3 +69,144 @@ pub const VertType = enum {
};
}
};
pub fn newGfx(device: *const Device, fixed_funcs: *const FixedFuncs, vert_layout: *const VertexLayout, layout: vk.PipelineLayout, render_pass: vk.RenderPass, subpass: ?u32, shaders: []const vk.PipelineShaderStageCreateInfo) !vk.Pipeline {
assert(layout != .null_handle);
assert(render_pass != .null_handle);
const vertex_input_info = vk.PipelineVertexInputStateCreateInfo{
.flags = .{},
.vertex_binding_description_count = @intCast(u32, vert_layout.bindings.len),
.p_vertex_binding_descriptions = vert_layout.bindings.ptr,
.vertex_attribute_description_count = @intCast(u32, vert_layout.attributes.len),
.p_vertex_attribute_descriptions = vert_layout.attributes.ptr,
};
const input_assembly = vk.PipelineInputAssemblyStateCreateInfo{
.flags = .{},
.primitive_restart_enable = 0,
.topology = fixed_funcs.topology,
};
const viewport_state = vk.PipelineViewportStateCreateInfo{
.flags = .{},
.viewport_count = 1,
.p_viewports = @ptrCast([*]const vk.Viewport, &fixed_funcs.viewport),
.scissor_count = 1,
.p_scissors = @ptrCast([*]const vk.Rect2D, &fixed_funcs.scissor),
};
const rasterizer = vk.PipelineRasterizationStateCreateInfo{
.flags = .{},
.depth_clamp_enable = @boolToInt(fixed_funcs.depth_clamp),
.rasterizer_discard_enable = 0,
.depth_bias_constant_factor = 0,
.depth_bias_enable = 0,
.depth_bias_clamp = 0,
.depth_bias_slope_factor = 0,
.polygon_mode = fixed_funcs.polygon_mode,
.cull_mode = fixed_funcs.cull_mode,
.front_face = fixed_funcs.front_face,
.line_width = 1,
};
const multisampling = vk.PipelineMultisampleStateCreateInfo{
.flags = .{},
.sample_shading_enable = 0,
.min_sample_shading = 0,
.p_sample_mask = null,
.alpha_to_coverage_enable = 0,
.alpha_to_one_enable = 0,
.rasterization_samples = .{ .@"1_bit" = true },
};
const depth_stencil = vk.PipelineDepthStencilStateCreateInfo{
.flags = .{},
.depth_test_enable = @boolToInt(fixed_funcs.depth_test_enable),
.depth_bounds_test_enable = 0,
.stencil_test_enable = 0,
.front = std.mem.zeroes(vk.StencilOpState),
.back = std.mem.zeroes(vk.StencilOpState),
.depth_write_enable = @boolToInt(fixed_funcs.depth_write_enable),
.depth_compare_op = fixed_funcs.depth_compare_op,
.min_depth_bounds = 0,
.max_depth_bounds = 1,
};
var color_blend_attachments = std.mem.zeroes([8]vk.PipelineColorBlendAttachmentState);
if (fixed_funcs.attachments) |attachments| {
for (attachments) |attachment, i| {
color_blend_attachments[i] = vk.PipelineColorBlendAttachmentState{
.blend_enable = @boolToInt(attachment.blend_enable),
.src_color_blend_factor = attachment.src_color_blend_factor,
.dst_color_blend_factor = attachment.dst_color_blend_factor,
.color_blend_op = attachment.color_blend_op,
.src_alpha_blend_factor = attachment.src_alpha_blend_factor,
.dst_alpha_blend_factor = attachment.dst_alpha_blend_factor,
.alpha_blend_op = attachment.alpha_blend_op,
.color_write_mask = attachment.color_wite_mask,
};
}
}
const color_blending = vk.PipelineColorBlendStateCreateInfo{
.flags = .{},
.logic_op_enable = 0,
.logic_op = .clear,
.blend_constants = std.mem.zeroes([4]f32),
.attachment_count = fixed_funcs.attachments.?.len,
.p_attachments = &color_blend_attachments,
};
const dynamic_states = [_]vk.DynamicState{
.viewport,
.scissor,
};
const dynamic_state_info = vk.PipelineDynamicStateCreateInfo{
.flags = .{},
.dynamic_state_count = dynamic_states.len,
.p_dynamic_states = &dynamic_states,
};
const pipeline_info = vk.GraphicsPipelineCreateInfo{
.flags = .{},
.stage_count = @intCast(u32, shaders.len),
.p_stages = shaders.ptr,
.p_vertex_input_state = &vertex_input_info,
.p_input_assembly_state = &input_assembly,
.p_tessellation_state = null,
.p_viewport_state = &viewport_state,
.p_rasterization_state = &rasterizer,
.p_multisample_state = &multisampling,
.p_depth_stencil_state = &depth_stencil,
.p_color_blend_state = &color_blending,
.p_dynamic_state = &dynamic_state_info,
.layout = layout,
.render_pass = render_pass,
.base_pipeline_handle = .null_handle,
.base_pipeline_index = 0,
.subpass = subpass orelse 0,
};
var pipelines: [*]vk.Pipeline = undefined;
_ = try device.dispatch.createGraphicsPipelines(device.handle, .null_handle, 1, @ptrCast([*]const vk.GraphicsPipelineCreateInfo, &pipeline_info), null, pipelines);
return pipelines[0];
}
pub fn newComp(device: *const Device, layout: vk.PipelineLayout, shader: *const vk.PipelineShaderStageCreateInfo) !vk.Pipeline {
assert(layout != .null_handle);
var pipelines: [*]vk.Pipeline = undefined;
_ = try device.dispatch.createComputePipelines(device.handle, .null_handle, 1, @ptrCast([*]const vk.ComputePipelineCreateInfo, &.{
.flags = .{},
.stage = shader,
.layout = layout,
}), null, pipelines);
return pipelines[0];
}
pub fn deinit(device: *const Device, pipeline: vk.Pipeline) void {
device.dispatch.destroyPipeline(device.handle, pipeline, null);
}

View file

@ -30,31 +30,31 @@ pub const Bid = struct {
stages: vk.ShaderStageFlags,
pub const TextureTable = struct {
const T1D = Bid{
pub const T1D = Bid{
.id = 0,
.desc_type = .combined_image_sampler,
.count = 64,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const T2D = Bid{
pub const T2D = Bid{
.id = T1D.id + 1,
.desc_type = .combined_image_sampler,
.count = 512,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const T3D = Bid{
pub const T3D = Bid{
.id = T2D.id + 1,
.desc_type = .combined_image_sampler,
.count = 64,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const TCube = Bid{
pub const TCube = Bid{
.id = T3D.id + 1,
.desc_type = .combined_image_sampler,
.count = 64,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const T2DArray = Bid{
pub const T2DArray = Bid{
.id = TCube.id + 1,
.desc_type = .combined_image_sampler,
.count = 64,
@ -62,26 +62,31 @@ pub const Bid = struct {
};
};
const Globals = Bid{
pub const Globals = Bid{
.id = TextureTable.T2DArray.id + 1,
.desc_type = .uniform_buffer,
.stages = .{}.complement(), // all
.stages = vk.ShaderStageFlags.complement(.{}), // all
};
const SceneLuminance = Bid{
pub const SceneLuminance = Bid{
.id = Globals.id + 1,
.desc_type = .combined_image_sampler,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const HistogramBuffer = Bid{
pub const RWSceneLuminance = Bid{
.id = SceneLuminance.id + 1,
.desc_type = .storage_image,
.stages = .{ .compute_bit = true },
};
const ExposureBuffer = Bid{
pub const HistogramBuffer = Bid{
.id = RWSceneLuminance.id + 1,
.desc_type = .storage_image,
.stages = .{ .compute_bit = true },
};
pub const ExposureBuffer = Bid{
.id = HistogramBuffer.id + 1,
.desc_type = .storage_buffer,
.stages = .{ .fragment_bit = true, .compute_bit = true },
};
const Count = ExposureBuffer.id + 1;
pub const Count = ExposureBuffer.id + 1;
};