src/sysgpu: move mach-sysgpu@d6ed118f54c4784f7ce01b70fc1b94f887fae1a8 package to here
Helps hexops/mach#1165 Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
e38a441b69
commit
e7b2ebef70
97 changed files with 51261 additions and 0 deletions
30
src/sysgpu/conventions.md
Normal file
30
src/sysgpu/conventions.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
### Object ordering
|
||||
|
||||
Backends should be a single file with object in the following order:
|
||||
|
||||
- Instance
|
||||
- Adapter
|
||||
- Surface
|
||||
- SurfaceCapabilities
|
||||
- Device
|
||||
- SwapChain
|
||||
- Buffer
|
||||
- Texture
|
||||
- TextureView
|
||||
- Sampler
|
||||
- BindGroupLayout
|
||||
- BindGroup
|
||||
- PipelineLayout
|
||||
- ShaderModule
|
||||
- ComputePipeline
|
||||
- RenderPipeline
|
||||
- CommandBuffer
|
||||
- CommandEncoder
|
||||
- ComputePassEncoder
|
||||
- RenderPassEncoder
|
||||
- RenderBundle
|
||||
- RenderBundleEncoder
|
||||
- Queue
|
||||
- QuerySet
|
||||
|
||||
Utility objects (e.g. StateTracker should come after the closest object that "owns" them.
|
||||
4088
src/sysgpu/d3d12.zig
Normal file
4088
src/sysgpu/d3d12.zig
Normal file
File diff suppressed because it is too large
Load diff
6
src/sysgpu/d3d12/c.zig
Normal file
6
src/sysgpu/d3d12/c.zig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub usingnamespace @cImport({
|
||||
@cInclude("d3d12.h");
|
||||
@cInclude("dxgi1_6.h");
|
||||
@cInclude("d3dcompiler.h");
|
||||
@cInclude("dxgidebug.h");
|
||||
});
|
||||
737
src/sysgpu/d3d12/conv.zig
Normal file
737
src/sysgpu/d3d12/conv.zig
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
const sysgpu = @import("../sysgpu/main.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
const c = @import("c.zig");
|
||||
|
||||
fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
|
||||
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
|
||||
}
|
||||
|
||||
pub fn winBool(b: bool) c.BOOL {
|
||||
return if (b) c.TRUE else c.FALSE;
|
||||
}
|
||||
|
||||
pub fn d3d12Blend(factor: sysgpu.BlendFactor) c.D3D12_BLEND {
|
||||
return switch (factor) {
|
||||
.zero => c.D3D12_BLEND_ZERO,
|
||||
.one => c.D3D12_BLEND_ONE,
|
||||
.src => c.D3D12_BLEND_SRC_COLOR,
|
||||
.one_minus_src => c.D3D12_BLEND_INV_SRC_COLOR,
|
||||
.src_alpha => c.D3D12_BLEND_SRC_ALPHA,
|
||||
.one_minus_src_alpha => c.D3D12_BLEND_INV_SRC_ALPHA,
|
||||
.dst => c.D3D12_BLEND_DEST_COLOR,
|
||||
.one_minus_dst => c.D3D12_BLEND_INV_DEST_COLOR,
|
||||
.dst_alpha => c.D3D12_BLEND_DEST_ALPHA,
|
||||
.one_minus_dst_alpha => c.D3D12_BLEND_INV_DEST_ALPHA,
|
||||
.src_alpha_saturated => c.D3D12_BLEND_SRC_ALPHA_SAT,
|
||||
.constant => c.D3D12_BLEND_BLEND_FACTOR,
|
||||
.one_minus_constant => c.D3D12_BLEND_INV_BLEND_FACTOR,
|
||||
.src1 => c.D3D12_BLEND_SRC1_COLOR,
|
||||
.one_minus_src1 => c.D3D12_BLEND_INV_SRC1_COLOR,
|
||||
.src1_alpha => c.D3D12_BLEND_SRC1_ALPHA,
|
||||
.one_minus_src1_alpha => c.D3D12_BLEND_INV_SRC1_ALPHA,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12BlendDesc(desc: *const sysgpu.RenderPipeline.Descriptor) c.D3D12_BLEND_DESC {
|
||||
var d3d12_targets = [_]c.D3D12_RENDER_TARGET_BLEND_DESC{d3d12RenderTargetBlendDesc(null)} ** 8;
|
||||
if (desc.fragment) |frag| {
|
||||
for (0..frag.target_count) |i| {
|
||||
const target = frag.targets.?[i];
|
||||
d3d12_targets[i] = d3d12RenderTargetBlendDesc(target);
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
.AlphaToCoverageEnable = winBool(desc.multisample.alpha_to_coverage_enabled == .true),
|
||||
.IndependentBlendEnable = c.TRUE,
|
||||
.RenderTarget = d3d12_targets,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12BlendOp(op: sysgpu.BlendOperation) c.D3D12_BLEND_OP {
|
||||
return switch (op) {
|
||||
.add => c.D3D12_BLEND_OP_ADD,
|
||||
.subtract => c.D3D12_BLEND_OP_SUBTRACT,
|
||||
.reverse_subtract => c.D3D12_BLEND_OP_REV_SUBTRACT,
|
||||
.min => c.D3D12_BLEND_OP_MIN,
|
||||
.max => c.D3D12_BLEND_OP_MAX,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12ComparisonFunc(func: sysgpu.CompareFunction) c.D3D12_COMPARISON_FUNC {
|
||||
return switch (func) {
|
||||
.undefined => unreachable,
|
||||
.never => c.D3D12_COMPARISON_FUNC_NEVER,
|
||||
.less => c.D3D12_COMPARISON_FUNC_LESS,
|
||||
.less_equal => c.D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
||||
.greater => c.D3D12_COMPARISON_FUNC_GREATER,
|
||||
.greater_equal => c.D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
||||
.equal => c.D3D12_COMPARISON_FUNC_EQUAL,
|
||||
.not_equal => c.D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
||||
.always => c.D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12CullMode(mode: sysgpu.CullMode) c.D3D12_CULL_MODE {
|
||||
return switch (mode) {
|
||||
.none => c.D3D12_CULL_MODE_NONE,
|
||||
.front => c.D3D12_CULL_MODE_FRONT,
|
||||
.back => c.D3D12_CULL_MODE_BACK,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12DepthStencilDesc(depth_stencil: ?*const sysgpu.DepthStencilState) c.D3D12_DEPTH_STENCIL_DESC {
|
||||
return if (depth_stencil) |ds| .{
|
||||
.DepthEnable = winBool(ds.depth_compare != .always or ds.depth_write_enabled == .true),
|
||||
.DepthWriteMask = if (ds.depth_write_enabled == .true) c.D3D12_DEPTH_WRITE_MASK_ALL else c.D3D12_DEPTH_WRITE_MASK_ZERO,
|
||||
.DepthFunc = d3d12ComparisonFunc(ds.depth_compare),
|
||||
.StencilEnable = winBool(stencilEnable(ds.stencil_front) or stencilEnable(ds.stencil_back)),
|
||||
.StencilReadMask = @intCast(ds.stencil_read_mask & 0xff),
|
||||
.StencilWriteMask = @intCast(ds.stencil_write_mask & 0xff),
|
||||
.FrontFace = d3d12DepthStencilOpDesc(ds.stencil_front),
|
||||
.BackFace = d3d12DepthStencilOpDesc(ds.stencil_back),
|
||||
} else .{
|
||||
.DepthEnable = c.FALSE,
|
||||
.DepthWriteMask = c.D3D12_DEPTH_WRITE_MASK_ZERO,
|
||||
.DepthFunc = c.D3D12_COMPARISON_FUNC_LESS,
|
||||
.StencilEnable = c.FALSE,
|
||||
.StencilReadMask = 0xff,
|
||||
.StencilWriteMask = 0xff,
|
||||
.FrontFace = d3d12DepthStencilOpDesc(null),
|
||||
.BackFace = d3d12DepthStencilOpDesc(null),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12DepthStencilOpDesc(opt_stencil: ?sysgpu.StencilFaceState) c.D3D12_DEPTH_STENCILOP_DESC {
|
||||
return if (opt_stencil) |stencil| .{
|
||||
.StencilFailOp = d3d12StencilOp(stencil.fail_op),
|
||||
.StencilDepthFailOp = d3d12StencilOp(stencil.depth_fail_op),
|
||||
.StencilPassOp = d3d12StencilOp(stencil.pass_op),
|
||||
.StencilFunc = d3d12ComparisonFunc(stencil.compare),
|
||||
} else .{
|
||||
.StencilFailOp = c.D3D12_STENCIL_OP_KEEP,
|
||||
.StencilDepthFailOp = c.D3D12_STENCIL_OP_KEEP,
|
||||
.StencilPassOp = c.D3D12_STENCIL_OP_KEEP,
|
||||
.StencilFunc = c.D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12DescriptorRangeType(entry: sysgpu.BindGroupLayout.Entry) c.D3D12_DESCRIPTOR_RANGE_TYPE {
|
||||
if (entry.buffer.type != .undefined) {
|
||||
return switch (entry.buffer.type) {
|
||||
.undefined => unreachable,
|
||||
.uniform => c.D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
|
||||
.storage => c.D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
|
||||
.read_only_storage => c.D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
|
||||
};
|
||||
} else if (entry.sampler.type != .undefined) {
|
||||
return c.D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
||||
} else if (entry.texture.sample_type != .undefined) {
|
||||
return c.D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
} else {
|
||||
// storage_texture
|
||||
return c.D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn d3d12FilterType(filter: sysgpu.FilterMode) c.D3D12_FILTER_TYPE {
|
||||
return switch (filter) {
|
||||
.nearest => c.D3D12_FILTER_TYPE_POINT,
|
||||
.linear => c.D3D12_FILTER_TYPE_LINEAR,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12FilterTypeForMipmap(filter: sysgpu.MipmapFilterMode) c.D3D12_FILTER_TYPE {
|
||||
return switch (filter) {
|
||||
.nearest => c.D3D12_FILTER_TYPE_POINT,
|
||||
.linear => c.D3D12_FILTER_TYPE_LINEAR,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12Filter(
|
||||
mag_filter: sysgpu.FilterMode,
|
||||
min_filter: sysgpu.FilterMode,
|
||||
mipmap_filter: sysgpu.MipmapFilterMode,
|
||||
max_anisotropy: u16,
|
||||
) c.D3D12_FILTER {
|
||||
var filter: c.D3D12_FILTER = 0;
|
||||
filter |= d3d12FilterType(min_filter) << c.D3D12_MIN_FILTER_SHIFT;
|
||||
filter |= d3d12FilterType(mag_filter) << c.D3D12_MAG_FILTER_SHIFT;
|
||||
filter |= d3d12FilterTypeForMipmap(mipmap_filter) << c.D3D12_MIP_FILTER_SHIFT;
|
||||
filter |= c.D3D12_FILTER_REDUCTION_TYPE_STANDARD << c.D3D12_FILTER_REDUCTION_TYPE_SHIFT;
|
||||
if (max_anisotropy > 1)
|
||||
filter |= c.D3D12_ANISOTROPIC_FILTERING_BIT;
|
||||
return filter;
|
||||
}
|
||||
|
||||
pub fn d3d12FrontCounterClockwise(face: sysgpu.FrontFace) c.BOOL {
|
||||
return switch (face) {
|
||||
.ccw => c.TRUE,
|
||||
.cw => c.FALSE,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12HeapType(usage: sysgpu.Buffer.UsageFlags) c.D3D12_HEAP_TYPE {
|
||||
return if (usage.map_write)
|
||||
c.D3D12_HEAP_TYPE_UPLOAD
|
||||
else if (usage.map_read)
|
||||
c.D3D12_HEAP_TYPE_READBACK
|
||||
else
|
||||
c.D3D12_HEAP_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
pub fn d3d12IndexBufferStripCutValue(strip_index_format: sysgpu.IndexFormat) c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE {
|
||||
return switch (strip_index_format) {
|
||||
.undefined => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED,
|
||||
.uint16 => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF,
|
||||
.uint32 => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12InputClassification(mode: sysgpu.VertexStepMode) c.D3D12_INPUT_CLASSIFICATION {
|
||||
return switch (mode) {
|
||||
.vertex => c.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
|
||||
.instance => c.D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA,
|
||||
.vertex_buffer_not_used => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12InputElementDesc(
|
||||
buffer_index: usize,
|
||||
layout: sysgpu.VertexBufferLayout,
|
||||
attr: sysgpu.VertexAttribute,
|
||||
) c.D3D12_INPUT_ELEMENT_DESC {
|
||||
return .{
|
||||
.SemanticName = "ATTR",
|
||||
.SemanticIndex = attr.shader_location,
|
||||
.Format = dxgiFormatForVertex(attr.format),
|
||||
.InputSlot = @intCast(buffer_index),
|
||||
.AlignedByteOffset = @intCast(attr.offset),
|
||||
.InputSlotClass = d3d12InputClassification(layout.step_mode),
|
||||
.InstanceDataStepRate = if (layout.step_mode == .instance) 1 else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12PrimitiveTopology(topology: sysgpu.PrimitiveTopology) c.D3D12_PRIMITIVE_TOPOLOGY {
|
||||
return switch (topology) {
|
||||
.point_list => c.D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
|
||||
.line_list => c.D3D_PRIMITIVE_TOPOLOGY_LINELIST,
|
||||
.line_strip => c.D3D_PRIMITIVE_TOPOLOGY_LINESTRIP,
|
||||
.triangle_list => c.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
|
||||
.triangle_strip => c.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12PrimitiveTopologyType(topology: sysgpu.PrimitiveTopology) c.D3D12_PRIMITIVE_TOPOLOGY_TYPE {
|
||||
return switch (topology) {
|
||||
.point_list => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,
|
||||
.line_list, .line_strip => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
|
||||
.triangle_list, .triangle_strip => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12RasterizerDesc(desc: *const sysgpu.RenderPipeline.Descriptor) c.D3D12_RASTERIZER_DESC {
|
||||
const primitive_depth_control = utils.findChained(
|
||||
sysgpu.PrimitiveDepthClipControl,
|
||||
desc.primitive.next_in_chain.generic,
|
||||
);
|
||||
|
||||
return .{
|
||||
.FillMode = c.D3D12_FILL_MODE_SOLID,
|
||||
.CullMode = d3d12CullMode(desc.primitive.cull_mode),
|
||||
.FrontCounterClockwise = d3d12FrontCounterClockwise(desc.primitive.front_face),
|
||||
.DepthBias = if (desc.depth_stencil) |ds| ds.depth_bias else 0,
|
||||
.DepthBiasClamp = if (desc.depth_stencil) |ds| ds.depth_bias_clamp else 0.0,
|
||||
.SlopeScaledDepthBias = if (desc.depth_stencil) |ds| ds.depth_bias_slope_scale else 0.0,
|
||||
.DepthClipEnable = winBool(if (primitive_depth_control) |x| x.unclipped_depth == .false else true),
|
||||
.MultisampleEnable = winBool(desc.multisample.count > 1),
|
||||
.AntialiasedLineEnable = c.FALSE,
|
||||
.ForcedSampleCount = 0,
|
||||
.ConservativeRaster = c.D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12RenderTargetBlendDesc(opt_target: ?sysgpu.ColorTargetState) c.D3D12_RENDER_TARGET_BLEND_DESC {
|
||||
var desc = c.D3D12_RENDER_TARGET_BLEND_DESC{
|
||||
.BlendEnable = c.FALSE,
|
||||
.LogicOpEnable = c.FALSE,
|
||||
.SrcBlend = c.D3D12_BLEND_ONE,
|
||||
.DestBlend = c.D3D12_BLEND_ZERO,
|
||||
.BlendOp = c.D3D12_BLEND_OP_ADD,
|
||||
.SrcBlendAlpha = c.D3D12_BLEND_ONE,
|
||||
.DestBlendAlpha = c.D3D12_BLEND_ZERO,
|
||||
.BlendOpAlpha = c.D3D12_BLEND_OP_ADD,
|
||||
.LogicOp = c.D3D12_LOGIC_OP_NOOP,
|
||||
.RenderTargetWriteMask = 0xf,
|
||||
};
|
||||
if (opt_target) |target| {
|
||||
desc.RenderTargetWriteMask = d3d12RenderTargetWriteMask(target.write_mask);
|
||||
if (target.blend) |blend| {
|
||||
desc.BlendEnable = c.TRUE;
|
||||
desc.SrcBlend = d3d12Blend(blend.color.src_factor);
|
||||
desc.DestBlend = d3d12Blend(blend.color.dst_factor);
|
||||
desc.BlendOp = d3d12BlendOp(blend.color.operation);
|
||||
desc.SrcBlendAlpha = d3d12Blend(blend.alpha.src_factor);
|
||||
desc.DestBlendAlpha = d3d12Blend(blend.alpha.dst_factor);
|
||||
desc.BlendOpAlpha = d3d12BlendOp(blend.alpha.operation);
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
pub fn d3d12RenderTargetWriteMask(mask: sysgpu.ColorWriteMaskFlags) c.UINT8 {
|
||||
var writeMask: c.INT = 0;
|
||||
if (mask.red)
|
||||
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_RED;
|
||||
if (mask.green)
|
||||
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_GREEN;
|
||||
if (mask.blue)
|
||||
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_BLUE;
|
||||
if (mask.alpha)
|
||||
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_ALPHA;
|
||||
return @intCast(writeMask);
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceSizeForBuffer(size: u64, usage: sysgpu.Buffer.UsageFlags) c.UINT64 {
|
||||
var resource_size = size;
|
||||
if (usage.uniform)
|
||||
resource_size = utils.alignUp(resource_size, 256);
|
||||
return resource_size;
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceStatesInitial(heap_type: c.D3D12_HEAP_TYPE, read_state: c.D3D12_RESOURCE_STATES) c.D3D12_RESOURCE_STATES {
|
||||
return switch (heap_type) {
|
||||
c.D3D12_HEAP_TYPE_UPLOAD => c.D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
c.D3D12_HEAP_TYPE_READBACK => c.D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
else => read_state,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceStatesForBufferRead(usage: sysgpu.Buffer.UsageFlags) c.D3D12_RESOURCE_STATES {
|
||||
var states: c.D3D12_RESOURCE_STATES = c.D3D12_RESOURCE_STATE_COMMON;
|
||||
if (usage.copy_src)
|
||||
states |= c.D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
if (usage.index)
|
||||
states |= c.D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
if (usage.vertex or usage.uniform)
|
||||
states |= c.D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
||||
if (usage.storage)
|
||||
states |= c.D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
||||
if (usage.indirect)
|
||||
states |= c.D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
|
||||
return states;
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceStatesForTextureRead(usage: sysgpu.Texture.UsageFlags) c.D3D12_RESOURCE_STATES {
|
||||
var states: c.D3D12_RESOURCE_STATES = c.D3D12_RESOURCE_STATE_COMMON;
|
||||
if (usage.copy_src)
|
||||
states |= c.D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
if (usage.texture_binding or usage.storage_binding)
|
||||
states |= c.D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
||||
return states;
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceFlagsForBuffer(usage: sysgpu.Buffer.UsageFlags) c.D3D12_RESOURCE_FLAGS {
|
||||
var flags: c.D3D12_RESOURCE_FLAGS = c.D3D12_RESOURCE_FLAG_NONE;
|
||||
if (usage.storage)
|
||||
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceFlagsForTexture(
|
||||
usage: sysgpu.Texture.UsageFlags,
|
||||
format: sysgpu.Texture.Format,
|
||||
) c.D3D12_RESOURCE_FLAGS {
|
||||
var flags: c.D3D12_RESOURCE_FLAGS = c.D3D12_RESOURCE_FLAG_NONE;
|
||||
if (usage.render_attachment) {
|
||||
if (utils.formatHasDepthOrStencil(format)) {
|
||||
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
} else {
|
||||
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
||||
}
|
||||
}
|
||||
if (usage.storage_binding)
|
||||
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
if (!usage.texture_binding and usage.render_attachment and utils.formatHasDepthOrStencil(format))
|
||||
flags |= c.D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn d3d12ResourceDimension(dimension: sysgpu.Texture.Dimension) c.D3D12_RESOURCE_DIMENSION {
|
||||
return switch (dimension) {
|
||||
.dimension_1d => c.D3D12_RESOURCE_DIMENSION_TEXTURE1D,
|
||||
.dimension_2d => c.D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
||||
.dimension_3d => c.D3D12_RESOURCE_DIMENSION_TEXTURE3D,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12RootParameterType(entry: sysgpu.BindGroupLayout.Entry) c.D3D12_ROOT_PARAMETER_TYPE {
|
||||
return switch (entry.buffer.type) {
|
||||
.undefined => unreachable,
|
||||
.uniform => c.D3D12_ROOT_PARAMETER_TYPE_CBV,
|
||||
.storage => c.D3D12_ROOT_PARAMETER_TYPE_UAV,
|
||||
.read_only_storage => c.D3D12_ROOT_PARAMETER_TYPE_SRV,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12ShaderBytecode(opt_blob: ?*c.ID3DBlob) c.D3D12_SHADER_BYTECODE {
|
||||
return if (opt_blob) |blob| .{
|
||||
.pShaderBytecode = blob.lpVtbl.*.GetBufferPointer.?(blob),
|
||||
.BytecodeLength = blob.lpVtbl.*.GetBufferSize.?(blob),
|
||||
} else .{ .pShaderBytecode = null, .BytecodeLength = 0 };
|
||||
}
|
||||
|
||||
pub fn d3d12SrvDimension(dimension: sysgpu.TextureView.Dimension, sample_count: u32) c.D3D12_SRV_DIMENSION {
|
||||
return switch (dimension) {
|
||||
.dimension_undefined => unreachable,
|
||||
.dimension_1d => c.D3D12_SRV_DIMENSION_TEXTURE1D,
|
||||
.dimension_2d => if (sample_count == 1) c.D3D12_SRV_DIMENSION_TEXTURE2D else c.D3D12_SRV_DIMENSION_TEXTURE2DMS,
|
||||
.dimension_2d_array => if (sample_count == 1) c.D3D12_SRV_DIMENSION_TEXTURE2DARRAY else c.D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY,
|
||||
.dimension_cube => c.D3D12_SRV_DIMENSION_TEXTURECUBE,
|
||||
.dimension_cube_array => c.D3D12_SRV_DIMENSION_TEXTURECUBEARRAY,
|
||||
.dimension_3d => c.D3D12_SRV_DIMENSION_TEXTURE3D,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12StencilOp(op: sysgpu.StencilOperation) c.D3D12_STENCIL_OP {
|
||||
return switch (op) {
|
||||
.keep => c.D3D12_STENCIL_OP_KEEP,
|
||||
.zero => c.D3D12_STENCIL_OP_ZERO,
|
||||
.replace => c.D3D12_STENCIL_OP_REPLACE,
|
||||
.invert => c.D3D12_STENCIL_OP_INVERT,
|
||||
.increment_clamp => c.D3D12_STENCIL_OP_INCR_SAT,
|
||||
.decrement_clamp => c.D3D12_STENCIL_OP_DECR_SAT,
|
||||
.increment_wrap => c.D3D12_STENCIL_OP_INCR,
|
||||
.decrement_wrap => c.D3D12_STENCIL_OP_DECR,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12StreamOutputDesc() c.D3D12_STREAM_OUTPUT_DESC {
|
||||
return .{
|
||||
.pSODeclaration = null,
|
||||
.NumEntries = 0,
|
||||
.pBufferStrides = null,
|
||||
.NumStrides = 0,
|
||||
.RasterizedStream = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12TextureAddressMode(address_mode: sysgpu.Sampler.AddressMode) c.D3D12_TEXTURE_ADDRESS_MODE {
|
||||
return switch (address_mode) {
|
||||
.repeat => c.D3D12_TEXTURE_ADDRESS_MODE_WRAP,
|
||||
.mirror_repeat => c.D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
|
||||
.clamp_to_edge => c.D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn d3d12UavDimension(dimension: sysgpu.TextureView.Dimension) c.D3D12_UAV_DIMENSION {
|
||||
return switch (dimension) {
|
||||
.dimension_undefined => unreachable,
|
||||
.dimension_1d => c.D3D12_UAV_DIMENSION_TEXTURE1D,
|
||||
.dimension_2d => c.D3D12_UAV_DIMENSION_TEXTURE2D,
|
||||
.dimension_2d_array => c.D3D12_UAV_DIMENSION_TEXTURE2DARRAY,
|
||||
.dimension_3d => c.D3D12_UAV_DIMENSION_TEXTURE3D,
|
||||
else => unreachable, // TODO - UAV cube maps?
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatForIndex(format: sysgpu.IndexFormat) c.DXGI_FORMAT {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => c.DXGI_FORMAT_R16_UINT,
|
||||
.uint32 => c.DXGI_FORMAT_R32_UINT,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatForTexture(format: sysgpu.Texture.Format) c.DXGI_FORMAT {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.r8_unorm => c.DXGI_FORMAT_R8_UNORM,
|
||||
.r8_snorm => c.DXGI_FORMAT_R8_SNORM,
|
||||
.r8_uint => c.DXGI_FORMAT_R8_UINT,
|
||||
.r8_sint => c.DXGI_FORMAT_R8_SINT,
|
||||
.r16_uint => c.DXGI_FORMAT_R16_UINT,
|
||||
.r16_sint => c.DXGI_FORMAT_R16_SINT,
|
||||
.r16_float => c.DXGI_FORMAT_R16_FLOAT,
|
||||
.rg8_unorm => c.DXGI_FORMAT_R8G8_UNORM,
|
||||
.rg8_snorm => c.DXGI_FORMAT_R8G8_SNORM,
|
||||
.rg8_uint => c.DXGI_FORMAT_R8G8_UINT,
|
||||
.rg8_sint => c.DXGI_FORMAT_R8G8_SINT,
|
||||
.r32_float => c.DXGI_FORMAT_R32_FLOAT,
|
||||
.r32_uint => c.DXGI_FORMAT_R32_UINT,
|
||||
.r32_sint => c.DXGI_FORMAT_R32_SINT,
|
||||
.rg16_uint => c.DXGI_FORMAT_R16G16_UINT,
|
||||
.rg16_sint => c.DXGI_FORMAT_R16G16_SINT,
|
||||
.rg16_float => c.DXGI_FORMAT_R16G16_FLOAT,
|
||||
.rgba8_unorm => c.DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
.rgba8_unorm_srgb => c.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
|
||||
.rgba8_snorm => c.DXGI_FORMAT_R8G8B8A8_SNORM,
|
||||
.rgba8_uint => c.DXGI_FORMAT_R8G8B8A8_UINT,
|
||||
.rgba8_sint => c.DXGI_FORMAT_R8G8B8A8_SINT,
|
||||
.bgra8_unorm => c.DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
.bgra8_unorm_srgb => c.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
|
||||
.rgb10_a2_unorm => c.DXGI_FORMAT_R10G10B10A2_UNORM,
|
||||
.rg11_b10_ufloat => c.DXGI_FORMAT_R11G11B10_FLOAT,
|
||||
.rgb9_e5_ufloat => c.DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
|
||||
.rg32_float => c.DXGI_FORMAT_R32G32_FLOAT,
|
||||
.rg32_uint => c.DXGI_FORMAT_R32G32_UINT,
|
||||
.rg32_sint => c.DXGI_FORMAT_R32G32_SINT,
|
||||
.rgba16_uint => c.DXGI_FORMAT_R16G16B16A16_UINT,
|
||||
.rgba16_sint => c.DXGI_FORMAT_R16G16B16A16_SINT,
|
||||
.rgba16_float => c.DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||
.rgba32_float => c.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
||||
.rgba32_uint => c.DXGI_FORMAT_R32G32B32A32_UINT,
|
||||
.rgba32_sint => c.DXGI_FORMAT_R32G32B32A32_SINT,
|
||||
.stencil8 => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
.depth16_unorm => c.DXGI_FORMAT_D16_UNORM,
|
||||
.depth24_plus => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
.depth24_plus_stencil8 => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
.depth32_float => c.DXGI_FORMAT_D32_FLOAT,
|
||||
.depth32_float_stencil8 => c.DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
|
||||
.bc1_rgba_unorm => c.DXGI_FORMAT_BC1_UNORM,
|
||||
.bc1_rgba_unorm_srgb => c.DXGI_FORMAT_BC1_UNORM_SRGB,
|
||||
.bc2_rgba_unorm => c.DXGI_FORMAT_BC2_UNORM,
|
||||
.bc2_rgba_unorm_srgb => c.DXGI_FORMAT_BC2_UNORM_SRGB,
|
||||
.bc3_rgba_unorm => c.DXGI_FORMAT_BC3_UNORM,
|
||||
.bc3_rgba_unorm_srgb => c.DXGI_FORMAT_BC3_UNORM_SRGB,
|
||||
.bc4_runorm => c.DXGI_FORMAT_BC4_UNORM,
|
||||
.bc4_rsnorm => c.DXGI_FORMAT_BC4_SNORM,
|
||||
.bc5_rg_unorm => c.DXGI_FORMAT_BC5_UNORM,
|
||||
.bc5_rg_snorm => c.DXGI_FORMAT_BC5_SNORM,
|
||||
.bc6_hrgb_ufloat => c.DXGI_FORMAT_BC6H_UF16,
|
||||
.bc6_hrgb_float => c.DXGI_FORMAT_BC6H_SF16,
|
||||
.bc7_rgba_unorm => c.DXGI_FORMAT_BC7_UNORM,
|
||||
.bc7_rgba_unorm_srgb => c.DXGI_FORMAT_BC7_UNORM_SRGB,
|
||||
.etc2_rgb8_unorm,
|
||||
.etc2_rgb8_unorm_srgb,
|
||||
.etc2_rgb8_a1_unorm,
|
||||
.etc2_rgb8_a1_unorm_srgb,
|
||||
.etc2_rgba8_unorm,
|
||||
.etc2_rgba8_unorm_srgb,
|
||||
.eacr11_unorm,
|
||||
.eacr11_snorm,
|
||||
.eacrg11_unorm,
|
||||
.eacrg11_snorm,
|
||||
.astc4x4_unorm,
|
||||
.astc4x4_unorm_srgb,
|
||||
.astc5x4_unorm,
|
||||
.astc5x4_unorm_srgb,
|
||||
.astc5x5_unorm,
|
||||
.astc5x5_unorm_srgb,
|
||||
.astc6x5_unorm,
|
||||
.astc6x5_unorm_srgb,
|
||||
.astc6x6_unorm,
|
||||
.astc6x6_unorm_srgb,
|
||||
.astc8x5_unorm,
|
||||
.astc8x5_unorm_srgb,
|
||||
.astc8x6_unorm,
|
||||
.astc8x6_unorm_srgb,
|
||||
.astc8x8_unorm,
|
||||
.astc8x8_unorm_srgb,
|
||||
.astc10x5_unorm,
|
||||
.astc10x5_unorm_srgb,
|
||||
.astc10x6_unorm,
|
||||
.astc10x6_unorm_srgb,
|
||||
.astc10x8_unorm,
|
||||
.astc10x8_unorm_srgb,
|
||||
.astc10x10_unorm,
|
||||
.astc10x10_unorm_srgb,
|
||||
.astc12x10_unorm,
|
||||
.astc12x10_unorm_srgb,
|
||||
.astc12x12_unorm,
|
||||
.astc12x12_unorm_srgb,
|
||||
=> unreachable,
|
||||
.r8_bg8_biplanar420_unorm => c.DXGI_FORMAT_NV12,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatForTextureResource(
|
||||
format: sysgpu.Texture.Format,
|
||||
usage: sysgpu.Texture.UsageFlags,
|
||||
view_format_count: usize,
|
||||
) c.DXGI_FORMAT {
|
||||
_ = usage;
|
||||
return if (view_format_count > 0)
|
||||
dxgiFormatTypeless(format)
|
||||
else
|
||||
dxgiFormatForTexture(format);
|
||||
}
|
||||
|
||||
pub fn dxgiFormatForTextureView(format: sysgpu.Texture.Format, aspect: sysgpu.Texture.Aspect) c.DXGI_FORMAT {
|
||||
return switch (aspect) {
|
||||
.all => switch (format) {
|
||||
.stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
|
||||
.depth16_unorm => c.DXGI_FORMAT_R16_UNORM,
|
||||
.depth24_plus => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
|
||||
.depth32_float => c.DXGI_FORMAT_R32_FLOAT,
|
||||
else => dxgiFormatForTexture(format),
|
||||
},
|
||||
.stencil_only => switch (format) {
|
||||
.stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
|
||||
.depth24_plus_stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
|
||||
.depth32_float_stencil8 => c.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT,
|
||||
else => unreachable,
|
||||
},
|
||||
.depth_only => switch (format) {
|
||||
.depth16_unorm => c.DXGI_FORMAT_R16_UNORM,
|
||||
.depth24_plus => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
|
||||
.depth24_plus_stencil8 => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
|
||||
.depth32_float => c.DXGI_FORMAT_R32_FLOAT,
|
||||
.depth32_float_stencil8 => c.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
|
||||
else => unreachable,
|
||||
},
|
||||
.plane0_only => unreachable,
|
||||
.plane1_only => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatForVertex(format: sysgpu.VertexFormat) c.DXGI_FORMAT {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint8x2 => c.DXGI_FORMAT_R8G8_UINT,
|
||||
.uint8x4 => c.DXGI_FORMAT_R8G8B8A8_UINT,
|
||||
.sint8x2 => c.DXGI_FORMAT_R8G8_SINT,
|
||||
.sint8x4 => c.DXGI_FORMAT_R8G8B8A8_SINT,
|
||||
.unorm8x2 => c.DXGI_FORMAT_R8G8_UNORM,
|
||||
.unorm8x4 => c.DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
.snorm8x2 => c.DXGI_FORMAT_R8G8_SNORM,
|
||||
.snorm8x4 => c.DXGI_FORMAT_R8G8B8A8_SNORM,
|
||||
.uint16x2 => c.DXGI_FORMAT_R16G16_UINT,
|
||||
.uint16x4 => c.DXGI_FORMAT_R16G16B16A16_UINT,
|
||||
.sint16x2 => c.DXGI_FORMAT_R16G16_SINT,
|
||||
.sint16x4 => c.DXGI_FORMAT_R16G16B16A16_SINT,
|
||||
.unorm16x2 => c.DXGI_FORMAT_R16G16_UNORM,
|
||||
.unorm16x4 => c.DXGI_FORMAT_R16G16B16A16_UNORM,
|
||||
.snorm16x2 => c.DXGI_FORMAT_R16G16_SNORM,
|
||||
.snorm16x4 => c.DXGI_FORMAT_R16G16B16A16_SNORM,
|
||||
.float16x2 => c.DXGI_FORMAT_R16G16_FLOAT,
|
||||
.float16x4 => c.DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||
.float32 => c.DXGI_FORMAT_R32_FLOAT,
|
||||
.float32x2 => c.DXGI_FORMAT_R32G32_FLOAT,
|
||||
.float32x3 => c.DXGI_FORMAT_R32G32B32_FLOAT,
|
||||
.float32x4 => c.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
||||
.uint32 => c.DXGI_FORMAT_R32_UINT,
|
||||
.uint32x2 => c.DXGI_FORMAT_R32G32_UINT,
|
||||
.uint32x3 => c.DXGI_FORMAT_R32G32B32_UINT,
|
||||
.uint32x4 => c.DXGI_FORMAT_R32G32B32A32_UINT,
|
||||
.sint32 => c.DXGI_FORMAT_R32_SINT,
|
||||
.sint32x2 => c.DXGI_FORMAT_R32G32_SINT,
|
||||
.sint32x3 => c.DXGI_FORMAT_R32G32B32_SINT,
|
||||
.sint32x4 => c.DXGI_FORMAT_R32G32B32A32_SINT,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatTypeless(format: sysgpu.Texture.Format) c.DXGI_FORMAT {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.r8_unorm, .r8_snorm, .r8_uint, .r8_sint => c.DXGI_FORMAT_R8_TYPELESS,
|
||||
.r16_uint, .r16_sint, .r16_float => c.DXGI_FORMAT_R16_TYPELESS,
|
||||
.rg8_unorm, .rg8_snorm, .rg8_uint, .rg8_sint => c.DXGI_FORMAT_R8G8_TYPELESS,
|
||||
.r32_float, .r32_uint, .r32_sint => c.DXGI_FORMAT_R32_TYPELESS,
|
||||
.rg16_uint, .rg16_sint, .rg16_float => c.DXGI_FORMAT_R16G16_TYPELESS,
|
||||
.rgba8_unorm, .rgba8_unorm_srgb, .rgba8_snorm, .rgba8_uint, .rgba8_sint => c.DXGI_FORMAT_R8G8B8A8_TYPELESS,
|
||||
.bgra8_unorm, .bgra8_unorm_srgb => c.DXGI_FORMAT_B8G8R8A8_TYPELESS,
|
||||
.rgb10_a2_unorm => c.DXGI_FORMAT_R10G10B10A2_TYPELESS,
|
||||
.rg11_b10_ufloat => c.DXGI_FORMAT_R11G11B10_FLOAT,
|
||||
.rgb9_e5_ufloat => c.DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
|
||||
.rg32_float, .rg32_uint, .rg32_sint => c.DXGI_FORMAT_R32G32_TYPELESS,
|
||||
.rgba16_uint, .rgba16_sint, .rgba16_float => c.DXGI_FORMAT_R16G16B16A16_TYPELESS,
|
||||
.rgba32_float, .rgba32_uint, .rgba32_sint => c.DXGI_FORMAT_R32G32B32A32_TYPELESS,
|
||||
.stencil8 => c.DXGI_FORMAT_R24G8_TYPELESS,
|
||||
.depth16_unorm => c.DXGI_FORMAT_R16_TYPELESS,
|
||||
.depth24_plus => c.DXGI_FORMAT_R24G8_TYPELESS,
|
||||
.depth24_plus_stencil8 => c.DXGI_FORMAT_R24G8_TYPELESS,
|
||||
.depth32_float => c.DXGI_FORMAT_R32_TYPELESS,
|
||||
.depth32_float_stencil8 => c.DXGI_FORMAT_R32G8X24_TYPELESS,
|
||||
.bc1_rgba_unorm, .bc1_rgba_unorm_srgb => c.DXGI_FORMAT_BC1_TYPELESS,
|
||||
.bc2_rgba_unorm, .bc2_rgba_unorm_srgb => c.DXGI_FORMAT_BC2_TYPELESS,
|
||||
.bc3_rgba_unorm, .bc3_rgba_unorm_srgb => c.DXGI_FORMAT_BC3_TYPELESS,
|
||||
.bc4_runorm, .bc4_rsnorm => c.DXGI_FORMAT_BC4_TYPELESS,
|
||||
.bc5_rg_unorm, .bc5_rg_snorm => c.DXGI_FORMAT_BC5_TYPELESS,
|
||||
.bc6_hrgb_ufloat, .bc6_hrgb_float => c.DXGI_FORMAT_BC6H_TYPELESS,
|
||||
.bc7_rgba_unorm, .bc7_rgba_unorm_srgb => c.DXGI_FORMAT_BC7_TYPELESS,
|
||||
.etc2_rgb8_unorm,
|
||||
.etc2_rgb8_unorm_srgb,
|
||||
.etc2_rgb8_a1_unorm,
|
||||
.etc2_rgb8_a1_unorm_srgb,
|
||||
.etc2_rgba8_unorm,
|
||||
.etc2_rgba8_unorm_srgb,
|
||||
.eacr11_unorm,
|
||||
.eacr11_snorm,
|
||||
.eacrg11_unorm,
|
||||
.eacrg11_snorm,
|
||||
.astc4x4_unorm,
|
||||
.astc4x4_unorm_srgb,
|
||||
.astc5x4_unorm,
|
||||
.astc5x4_unorm_srgb,
|
||||
.astc5x5_unorm,
|
||||
.astc5x5_unorm_srgb,
|
||||
.astc6x5_unorm,
|
||||
.astc6x5_unorm_srgb,
|
||||
.astc6x6_unorm,
|
||||
.astc6x6_unorm_srgb,
|
||||
.astc8x5_unorm,
|
||||
.astc8x5_unorm_srgb,
|
||||
.astc8x6_unorm,
|
||||
.astc8x6_unorm_srgb,
|
||||
.astc8x8_unorm,
|
||||
.astc8x8_unorm_srgb,
|
||||
.astc10x5_unorm,
|
||||
.astc10x5_unorm_srgb,
|
||||
.astc10x6_unorm,
|
||||
.astc10x6_unorm_srgb,
|
||||
.astc10x8_unorm,
|
||||
.astc10x8_unorm_srgb,
|
||||
.astc10x10_unorm,
|
||||
.astc10x10_unorm_srgb,
|
||||
.astc12x10_unorm,
|
||||
.astc12x10_unorm_srgb,
|
||||
.astc12x12_unorm,
|
||||
.astc12x12_unorm_srgb,
|
||||
=> unreachable,
|
||||
.r8_bg8_biplanar420_unorm => c.DXGI_FORMAT_NV12,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiFormatIsTypeless(format: c.DXGI_FORMAT) bool {
|
||||
return switch (format) {
|
||||
c.DXGI_FORMAT_R32G32B32A32_TYPELESS,
|
||||
c.DXGI_FORMAT_R32G32B32_TYPELESS,
|
||||
c.DXGI_FORMAT_R16G16B16A16_TYPELESS,
|
||||
c.DXGI_FORMAT_R32G32_TYPELESS,
|
||||
c.DXGI_FORMAT_R32G8X24_TYPELESS,
|
||||
c.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
|
||||
c.DXGI_FORMAT_R10G10B10A2_TYPELESS,
|
||||
c.DXGI_FORMAT_R8G8B8A8_TYPELESS,
|
||||
c.DXGI_FORMAT_R16G16_TYPELESS,
|
||||
c.DXGI_FORMAT_R32_TYPELESS,
|
||||
c.DXGI_FORMAT_R24G8_TYPELESS,
|
||||
c.DXGI_FORMAT_R8G8_TYPELESS,
|
||||
c.DXGI_FORMAT_R16_TYPELESS,
|
||||
c.DXGI_FORMAT_R8_TYPELESS,
|
||||
c.DXGI_FORMAT_BC1_TYPELESS,
|
||||
c.DXGI_FORMAT_BC2_TYPELESS,
|
||||
c.DXGI_FORMAT_BC3_TYPELESS,
|
||||
c.DXGI_FORMAT_BC4_TYPELESS,
|
||||
c.DXGI_FORMAT_BC5_TYPELESS,
|
||||
c.DXGI_FORMAT_B8G8R8A8_TYPELESS,
|
||||
c.DXGI_FORMAT_BC6H_TYPELESS,
|
||||
c.DXGI_FORMAT_BC7_TYPELESS,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dxgiUsage(usage: sysgpu.Texture.UsageFlags) c.DXGI_USAGE {
|
||||
var dxgi_usage: c.DXGI_USAGE = 0;
|
||||
if (usage.texture_binding)
|
||||
dxgi_usage |= c.DXGI_USAGE_SHADER_INPUT;
|
||||
if (usage.storage_binding)
|
||||
dxgi_usage |= c.DXGI_USAGE_UNORDERED_ACCESS;
|
||||
if (usage.render_attachment)
|
||||
dxgi_usage |= c.DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
return dxgi_usage;
|
||||
}
|
||||
44
src/sysgpu/d3d12/notes.md
Normal file
44
src/sysgpu/d3d12/notes.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
### Interface Support
|
||||
|
||||
Windows release version needed to use various functionality in DXGI and D3D12.
|
||||
|
||||
DXGI
|
||||
- 1.4 - baseline
|
||||
- 1.5 - 1607
|
||||
- 1.6 - 1703/1803/1809
|
||||
|
||||
CreateDXGIFactory
|
||||
|
||||
- CreateDXGIFactory2 - baseline
|
||||
|
||||
DXGIGetDebugInterface
|
||||
|
||||
- DXGIGetDebugInterface1 - baseline
|
||||
|
||||
IDXGIAdapter
|
||||
|
||||
- IDXGIAdapter3 - baseline
|
||||
- IDXGIAdapter4 - 1703
|
||||
|
||||
IDXGIDevice
|
||||
|
||||
- IDXGIDevice3 - baseline
|
||||
- IDXGIDevice4 - 1607
|
||||
|
||||
IDXGIFactory
|
||||
|
||||
- IDXGIFactory4 - baseline
|
||||
- IDXGIFactory5 - 1607
|
||||
- IDXGIFactory6 - 1803
|
||||
- IDXGIFactory7 - 1809
|
||||
|
||||
IDXGIOutput
|
||||
|
||||
- IDXGIOutput4 - baseline
|
||||
- IDXGIOutput5 - 1607
|
||||
- IDXGIOutput6 - 1703
|
||||
|
||||
IDXGISwapChain
|
||||
|
||||
- IDXGISwapChain3 - baseline
|
||||
- IDXGISwapChain4 - 1607
|
||||
576
src/sysgpu/gpu_allocator.zig
Normal file
576
src/sysgpu/gpu_allocator.zig
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
FreeingZeroSizeAllocation,
|
||||
InvalidAllocation,
|
||||
NoCompatibleMemoryFound,
|
||||
Other,
|
||||
};
|
||||
|
||||
pub const Allocation = struct {
|
||||
offset: u64,
|
||||
chunk: u64,
|
||||
};
|
||||
|
||||
pub const Allocator = union(enum) {
|
||||
offset_allocator: OffsetAllocator,
|
||||
dedicated_block_allocator: DedicatedBlockAllocator,
|
||||
|
||||
pub fn initOffsetAllocator(
|
||||
allocator: std.mem.Allocator,
|
||||
size: u32,
|
||||
max_allocations: ?u32,
|
||||
) std.mem.Allocator.Error!Allocator {
|
||||
return .{
|
||||
.offset_allocator = try OffsetAllocator.init(
|
||||
allocator,
|
||||
size,
|
||||
max_allocations,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initDedicatedBlockAllocator(
|
||||
size: u64,
|
||||
) std.mem.Allocator.Error!Allocator {
|
||||
return .{
|
||||
.dedicated_block_allocator = try DedicatedBlockAllocator.init(
|
||||
size,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Allocator) void {
|
||||
switch (self.*) {
|
||||
inline else => |*allocator| allocator.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(self: *Allocator) std.mem.Allocator.Error!void {
|
||||
switch (self.*) {
|
||||
inline else => |allocator| allocator.reset(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
self: *Allocator,
|
||||
size: u32,
|
||||
) Error!Allocation {
|
||||
return switch (self.*) {
|
||||
inline else => |*allocator| allocator.allocate(size),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn free(self: *Allocator, allocation: Allocation) Error!void {
|
||||
return switch (self.*) {
|
||||
inline else => |*allocator| allocator.free(allocation),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getSize(self: *const Allocator) u64 {
|
||||
return switch (self.*) {
|
||||
inline else => |allocator| allocator.getSize(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getAllocated(self: *const Allocator) u64 {
|
||||
return switch (self.*) {
|
||||
inline else => |allocator| allocator.getAllocated(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn availableMemory(self: *const Allocator) u64 {
|
||||
return self.getSize() - self.getAllocated();
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: *const Allocator) bool {
|
||||
return self.getAllocated() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const DedicatedBlockAllocator = struct {
|
||||
size: u64,
|
||||
allocated: u64,
|
||||
|
||||
pub fn init(
|
||||
size: u64,
|
||||
) std.mem.Allocator.Error!DedicatedBlockAllocator {
|
||||
return .{
|
||||
.size = size,
|
||||
.allocated = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DedicatedBlockAllocator) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
self: *DedicatedBlockAllocator,
|
||||
size: u32,
|
||||
) Error!Allocation {
|
||||
if (self.allocated != 0) {
|
||||
return Error.OutOfMemory;
|
||||
}
|
||||
|
||||
if (self.size != size) {
|
||||
return Error.OutOfMemory;
|
||||
}
|
||||
|
||||
self.allocated = size;
|
||||
|
||||
return .{
|
||||
.offset = 0,
|
||||
.chunk = 1,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn free(self: *DedicatedBlockAllocator, allocation: Allocation) Error!void {
|
||||
_ = allocation;
|
||||
self.allocated = 0;
|
||||
}
|
||||
|
||||
pub fn getSize(self: *const DedicatedBlockAllocator) u64 {
|
||||
return self.size;
|
||||
}
|
||||
|
||||
pub fn getAllocated(self: *const DedicatedBlockAllocator) u64 {
|
||||
return self.allocated;
|
||||
}
|
||||
};
|
||||
|
||||
// OffsetAllocator from https://github.com/sebbbi/OffsetAllocator
|
||||
// rewritten in zig
|
||||
pub const OffsetAllocator = struct {
|
||||
const NodeIndex = u32;
|
||||
|
||||
const Node = struct {
|
||||
data_offset: u32 = 0,
|
||||
data_size: u32 = 0,
|
||||
bin_list_prev: ?NodeIndex = null,
|
||||
bin_list_next: ?NodeIndex = null,
|
||||
neighbour_prev: ?NodeIndex = null,
|
||||
neighbour_next: ?NodeIndex = null,
|
||||
used: bool = false,
|
||||
};
|
||||
|
||||
const num_top_bins: u32 = 32;
|
||||
const bins_per_leaf: u32 = 8;
|
||||
const top_bins_index_shift: u32 = 3;
|
||||
const lead_bins_index_mask: u32 = 0x7;
|
||||
const num_leaf_bins: u32 = num_top_bins * bins_per_leaf;
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
size: u32,
|
||||
max_allocations: u32,
|
||||
free_storage: u32 = 0,
|
||||
|
||||
used_bins_top: u32 = 0,
|
||||
used_bins: [num_top_bins]u8 = undefined,
|
||||
bin_indices: [num_leaf_bins]?NodeIndex = undefined,
|
||||
|
||||
nodes: ?[]Node,
|
||||
free_nodes: ?[]NodeIndex,
|
||||
free_offset: u32 = 0,
|
||||
|
||||
const SmallFloat = struct {
|
||||
const mantissa_bits: u32 = 3;
|
||||
const mantissa_value: u32 = 1 << mantissa_bits;
|
||||
const mantissa_mask: u32 = mantissa_value - 1;
|
||||
|
||||
pub fn toFloatRoundUp(size: u32) u32 {
|
||||
var exp: u32 = 0;
|
||||
var mantissa: u32 = 0;
|
||||
|
||||
if (size < mantissa_value) {
|
||||
mantissa = size;
|
||||
} else {
|
||||
const leading_zeros = @clz(size);
|
||||
const highestSetBit = 31 - leading_zeros;
|
||||
|
||||
const mantissa_start_bit = highestSetBit - mantissa_bits;
|
||||
exp = mantissa_start_bit + 1;
|
||||
mantissa = (size >> @as(u5, @truncate(mantissa_start_bit))) & mantissa_mask;
|
||||
|
||||
const low_bits_mask = (@as(u32, 1) << @as(u5, @truncate(mantissa_start_bit))) - 1;
|
||||
if ((size & low_bits_mask) != 0) {
|
||||
mantissa += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (exp << mantissa_bits) + mantissa;
|
||||
}
|
||||
|
||||
pub fn toFloatRoundDown(size: u32) u32 {
|
||||
var exp: u32 = 0;
|
||||
var mantissa: u32 = 0;
|
||||
|
||||
if (size < mantissa_value) {
|
||||
mantissa = size;
|
||||
} else {
|
||||
const leading_zeros = @clz(size);
|
||||
const highestSetBit = 31 - leading_zeros;
|
||||
|
||||
const mantissa_start_bit = highestSetBit - mantissa_bits;
|
||||
exp = mantissa_start_bit + 1;
|
||||
mantissa = (size >> @as(u5, @truncate(mantissa_start_bit))) & mantissa_mask;
|
||||
}
|
||||
|
||||
return (exp << mantissa_bits) | mantissa;
|
||||
}
|
||||
};
|
||||
|
||||
fn findLowestSetBitAfter(v: u32, start_idx: u32) ?u32 {
|
||||
const mask_before_start_index: u32 = (@as(u32, 1) << @as(u5, @truncate(start_idx))) - 1;
|
||||
const mask_after_start_index: u32 = ~mask_before_start_index;
|
||||
const bits_after: u32 = v & mask_after_start_index;
|
||||
if (bits_after == 0) return null;
|
||||
return @ctz(bits_after);
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, size: u32, max_allocations: ?u32) std.mem.Allocator.Error!OffsetAllocator {
|
||||
var self = OffsetAllocator{
|
||||
.allocator = allocator,
|
||||
.size = size,
|
||||
.max_allocations = max_allocations orelse 128 * 1024,
|
||||
.nodes = null,
|
||||
.free_nodes = null,
|
||||
};
|
||||
try self.reset();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn reset(self: *OffsetAllocator) std.mem.Allocator.Error!void {
|
||||
self.free_storage = 0;
|
||||
self.used_bins_top = 0;
|
||||
self.free_offset = self.max_allocations - 1;
|
||||
|
||||
for (0..num_top_bins) |i| {
|
||||
self.used_bins[i] = 0;
|
||||
}
|
||||
|
||||
for (0..num_leaf_bins) |i| {
|
||||
self.bin_indices[i] = null;
|
||||
}
|
||||
|
||||
if (self.nodes) |nodes| {
|
||||
self.allocator.free(nodes);
|
||||
self.nodes = null;
|
||||
}
|
||||
if (self.free_nodes) |free_nodes| {
|
||||
self.allocator.free(free_nodes);
|
||||
self.free_nodes = null;
|
||||
}
|
||||
|
||||
self.nodes = try self.allocator.alloc(Node, self.max_allocations);
|
||||
self.free_nodes = try self.allocator.alloc(NodeIndex, self.max_allocations);
|
||||
|
||||
for (0..self.max_allocations) |i| {
|
||||
self.free_nodes.?[i] = self.max_allocations - @as(u32, @truncate(i)) - 1;
|
||||
}
|
||||
|
||||
_ = self.insertNodeIntoBin(self.size, 0);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *OffsetAllocator) void {
|
||||
if (self.nodes) |nodes| {
|
||||
self.allocator.free(nodes);
|
||||
self.nodes = null;
|
||||
}
|
||||
if (self.free_nodes) |free_nodes| {
|
||||
self.allocator.free(free_nodes);
|
||||
self.free_nodes = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
self: *OffsetAllocator,
|
||||
size: u32,
|
||||
) Error!Allocation {
|
||||
if (self.free_offset == 0) {
|
||||
return Error.OutOfMemory;
|
||||
}
|
||||
|
||||
const min_bin_index = SmallFloat.toFloatRoundUp(@intCast(size));
|
||||
|
||||
const min_top_bin_index: u32 = min_bin_index >> top_bins_index_shift;
|
||||
const min_leaf_bin_index: u32 = min_bin_index & lead_bins_index_mask;
|
||||
|
||||
var top_bin_index = min_top_bin_index;
|
||||
var leaf_bin_index: ?u32 = null;
|
||||
|
||||
if ((self.used_bins_top & (@as(u32, 1) << @as(u5, @truncate(top_bin_index)))) != 0) {
|
||||
leaf_bin_index = findLowestSetBitAfter(self.used_bins[top_bin_index], min_leaf_bin_index);
|
||||
}
|
||||
|
||||
if (leaf_bin_index == null) {
|
||||
const found_top_bin_index = findLowestSetBitAfter(self.used_bins_top, min_top_bin_index + 1);
|
||||
if (found_top_bin_index == null) {
|
||||
return Error.OutOfMemory;
|
||||
}
|
||||
top_bin_index = found_top_bin_index.?;
|
||||
leaf_bin_index = @ctz(self.used_bins[top_bin_index]);
|
||||
}
|
||||
|
||||
const bin_index = (top_bin_index << top_bins_index_shift) | leaf_bin_index.?;
|
||||
|
||||
const node_index = self.bin_indices[bin_index].?;
|
||||
const node = &self.nodes.?[node_index];
|
||||
const node_total_size = node.data_size;
|
||||
node.data_size = @intCast(size);
|
||||
node.used = true;
|
||||
self.bin_indices[bin_index] = node.bin_list_next;
|
||||
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = null;
|
||||
self.free_storage -= node_total_size;
|
||||
|
||||
// debug
|
||||
// std.debug.print("free storage: {} ({}) (allocate)\n", .{ self.free_storage, node_total_size });
|
||||
|
||||
if (self.bin_indices[bin_index] == null) {
|
||||
self.used_bins[top_bin_index] &= @as(u8, @truncate(~(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index.?)))));
|
||||
if (self.used_bins[top_bin_index] == 0) {
|
||||
self.used_bins_top &= ~(@as(u32, 1) << @as(u5, @truncate(top_bin_index)));
|
||||
}
|
||||
}
|
||||
|
||||
const remainder_size = node_total_size - size;
|
||||
if (remainder_size > 0) {
|
||||
const new_node_index = self.insertNodeIntoBin(@intCast(remainder_size), @intCast(node.data_offset + size));
|
||||
if (node.neighbour_next) |nnn| self.nodes.?[nnn].neighbour_prev = new_node_index;
|
||||
self.nodes.?[new_node_index].neighbour_prev = node_index;
|
||||
self.nodes.?[new_node_index].neighbour_next = node.neighbour_next;
|
||||
node.neighbour_next = new_node_index;
|
||||
}
|
||||
|
||||
return .{
|
||||
.offset = node.data_offset,
|
||||
.chunk = node_index,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn free(self: *OffsetAllocator, allocation: Allocation) Error!void {
|
||||
if (self.nodes == null) {
|
||||
return Error.InvalidAllocation;
|
||||
}
|
||||
|
||||
const node_index = allocation.chunk;
|
||||
const node = &self.nodes.?[node_index];
|
||||
if (!node.used) {
|
||||
return Error.InvalidAllocation;
|
||||
}
|
||||
|
||||
var offset = node.data_offset;
|
||||
var size = node.data_size;
|
||||
|
||||
if (node.neighbour_prev != null and self.nodes.?[node.neighbour_prev.?].used == false) {
|
||||
const prev_node = &self.nodes.?[node.neighbour_prev.?];
|
||||
offset = prev_node.data_offset;
|
||||
size += prev_node.data_size;
|
||||
|
||||
self.removeNodeFromBin(node.neighbour_prev.?);
|
||||
|
||||
std.debug.assert(prev_node.neighbour_next == @as(u32, @truncate(node_index)));
|
||||
node.neighbour_prev = prev_node.neighbour_prev;
|
||||
}
|
||||
|
||||
if (node.neighbour_next != null and self.nodes.?[node.neighbour_next.?].used == false) {
|
||||
const next_node = &self.nodes.?[node.neighbour_next.?];
|
||||
size += next_node.data_size;
|
||||
|
||||
self.removeNodeFromBin(node.neighbour_next.?);
|
||||
|
||||
std.debug.assert(next_node.neighbour_prev == @as(u32, @truncate(node_index)));
|
||||
node.neighbour_next = next_node.neighbour_next;
|
||||
}
|
||||
|
||||
const neighbour_prev = node.neighbour_prev;
|
||||
const neighbour_next = node.neighbour_next;
|
||||
|
||||
// debug
|
||||
// std.debug.print("putting node {} into freelist[{}] (free)\n", .{ node_index, self.free_offset + 1 });
|
||||
|
||||
self.free_offset += 1;
|
||||
self.free_nodes.?[self.free_offset] = @intCast(node_index);
|
||||
|
||||
const combined_node_index = self.insertNodeIntoBin(size, offset);
|
||||
if (neighbour_next) |nn| {
|
||||
self.nodes.?[combined_node_index].neighbour_next = neighbour_next;
|
||||
self.nodes.?[nn].neighbour_prev = combined_node_index;
|
||||
}
|
||||
if (neighbour_prev) |np| {
|
||||
self.nodes.?[combined_node_index].neighbour_prev = neighbour_prev;
|
||||
self.nodes.?[np].neighbour_next = combined_node_index;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insertNodeIntoBin(self: *OffsetAllocator, size: u32, data_offset: u32) u32 {
|
||||
const bin_index = SmallFloat.toFloatRoundDown(size);
|
||||
|
||||
const top_bin_index: u32 = bin_index >> top_bins_index_shift;
|
||||
const leaf_bin_index: u32 = bin_index & lead_bins_index_mask;
|
||||
|
||||
if (self.bin_indices[bin_index] == null) {
|
||||
self.used_bins[top_bin_index] |= @as(u8, @truncate(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index))));
|
||||
self.used_bins_top |= @as(u32, 1) << @as(u5, @truncate(top_bin_index));
|
||||
}
|
||||
|
||||
const top_node_index = self.bin_indices[bin_index];
|
||||
const node_index = self.free_nodes.?[self.free_offset];
|
||||
self.free_offset -= 1;
|
||||
|
||||
// debug
|
||||
// std.debug.print("getting node {} from freelist[{}]\n", .{ node_index, self.free_offset + 1 });
|
||||
|
||||
self.nodes.?[node_index] = .{
|
||||
.data_offset = data_offset,
|
||||
.data_size = size,
|
||||
.bin_list_next = top_node_index,
|
||||
};
|
||||
if (top_node_index) |tni| self.nodes.?[tni].bin_list_prev = node_index;
|
||||
self.bin_indices[bin_index] = node_index;
|
||||
|
||||
self.free_storage += size;
|
||||
|
||||
// debug
|
||||
// std.debug.print("free storage: {} ({}) (insertNodeIntoBin)\n", .{ self.free_storage, size });
|
||||
|
||||
return node_index;
|
||||
}
|
||||
|
||||
pub fn removeNodeFromBin(self: *OffsetAllocator, node_index: NodeIndex) void {
|
||||
const node = &self.nodes.?[node_index];
|
||||
|
||||
if (node.bin_list_prev) |blp| {
|
||||
self.nodes.?[blp].bin_list_next = node.bin_list_next;
|
||||
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = node.bin_list_prev;
|
||||
} else {
|
||||
const bin_index = SmallFloat.toFloatRoundDown(node.data_size);
|
||||
const top_bin_index: u32 = bin_index >> top_bins_index_shift;
|
||||
const leaf_bin_index: u32 = bin_index & lead_bins_index_mask;
|
||||
|
||||
self.bin_indices[bin_index] = node.bin_list_next;
|
||||
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = null;
|
||||
|
||||
if (self.bin_indices[bin_index] == null) {
|
||||
self.used_bins[top_bin_index] &= @as(u8, @truncate(~(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index)))));
|
||||
|
||||
if (self.used_bins[top_bin_index] == 0) {
|
||||
self.used_bins_top &= ~(@as(u32, 1) << @as(u5, @truncate(top_bin_index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debug
|
||||
// std.debug.print("putting node {} into freelist[{}] (removeNodeFromBin)\n", .{ node_index, self.free_offset + 1 });
|
||||
self.free_offset += 1;
|
||||
self.free_nodes.?[self.free_offset] = node_index;
|
||||
|
||||
self.free_storage -= node.data_size;
|
||||
|
||||
// debug
|
||||
// std.debug.print("free storage: {} ({}) (removeNodeFromBin)\n", .{ self.free_storage, node.data_size });
|
||||
}
|
||||
|
||||
pub fn getSize(self: *const OffsetAllocator) u64 {
|
||||
return self.size;
|
||||
}
|
||||
|
||||
pub fn getAllocated(self: *const OffsetAllocator) u64 {
|
||||
return self.size - self.free_storage;
|
||||
}
|
||||
};
|
||||
|
||||
test "basic" {
|
||||
var allocator = try Allocator.initOffsetAllocator(
|
||||
std.testing.allocator,
|
||||
1024 * 1024 * 256,
|
||||
null,
|
||||
);
|
||||
defer allocator.deinit();
|
||||
|
||||
const a = try allocator.allocate(1337);
|
||||
const offset = a.offset;
|
||||
try std.testing.expectEqual(@as(u64, 0), offset);
|
||||
try allocator.free(a);
|
||||
}
|
||||
|
||||
test "allocate" {
|
||||
var allocator = try Allocator.initOffsetAllocator(
|
||||
std.testing.allocator,
|
||||
1024 * 1024 * 256,
|
||||
null,
|
||||
);
|
||||
defer allocator.deinit();
|
||||
|
||||
{
|
||||
const a = try allocator.allocate(0);
|
||||
try std.testing.expectEqual(@as(u64, 0), a.offset);
|
||||
|
||||
const b = try allocator.allocate(1);
|
||||
try std.testing.expectEqual(@as(u64, 0), b.offset);
|
||||
|
||||
const c = try allocator.allocate(123);
|
||||
try std.testing.expectEqual(@as(u64, 1), c.offset);
|
||||
|
||||
const d = try allocator.allocate(1234);
|
||||
try std.testing.expectEqual(@as(u64, 124), d.offset);
|
||||
|
||||
try allocator.free(a);
|
||||
try allocator.free(b);
|
||||
try allocator.free(c);
|
||||
try allocator.free(d);
|
||||
|
||||
const validate = try allocator.allocate(1024 * 1024 * 256);
|
||||
try std.testing.expectEqual(@as(u64, 0), validate.offset);
|
||||
try allocator.free(validate);
|
||||
}
|
||||
|
||||
{
|
||||
const a = try allocator.allocate(1024);
|
||||
try std.testing.expectEqual(@as(u64, 0), a.offset);
|
||||
|
||||
const b = try allocator.allocate(3456);
|
||||
try std.testing.expectEqual(@as(u64, 1024), b.offset);
|
||||
|
||||
try allocator.free(a);
|
||||
|
||||
const c = try allocator.allocate(1024);
|
||||
try std.testing.expectEqual(@as(u64, 0), c.offset);
|
||||
|
||||
try allocator.free(b);
|
||||
try allocator.free(c);
|
||||
|
||||
const validate = try allocator.allocate(1024 * 1024 * 256);
|
||||
try std.testing.expectEqual(@as(u64, 0), validate.offset);
|
||||
try allocator.free(validate);
|
||||
}
|
||||
|
||||
{
|
||||
const a = try allocator.allocate(1024);
|
||||
try std.testing.expectEqual(@as(u64, 0), a.offset);
|
||||
|
||||
const b = try allocator.allocate(3456);
|
||||
try std.testing.expectEqual(@as(u64, 1024), b.offset);
|
||||
|
||||
try allocator.free(a);
|
||||
|
||||
const c = try allocator.allocate(2345);
|
||||
try std.testing.expectEqual(@as(u64, 1024 + 3456), c.offset);
|
||||
|
||||
const d = try allocator.allocate(456);
|
||||
try std.testing.expectEqual(@as(u64, 0), d.offset);
|
||||
|
||||
const e = try allocator.allocate(512);
|
||||
try std.testing.expectEqual(@as(u64, 456), e.offset);
|
||||
|
||||
try allocator.free(b);
|
||||
try allocator.free(c);
|
||||
try allocator.free(d);
|
||||
try allocator.free(e);
|
||||
|
||||
const validate = try allocator.allocate(1024 * 1024 * 256);
|
||||
try std.testing.expectEqual(@as(u64, 0), validate.offset);
|
||||
try allocator.free(validate);
|
||||
}
|
||||
}
|
||||
34
src/sysgpu/limits.zig
Normal file
34
src/sysgpu/limits.zig
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
pub const max_texture_dimension1d: u32 = 8192;
|
||||
pub const max_texture_dimension2d: u32 = 8192;
|
||||
pub const max_texture_dimension3d: u32 = 2048;
|
||||
pub const max_texture_array_layers: u32 = 256;
|
||||
pub const max_bind_groups: u32 = 4;
|
||||
pub const max_bind_groups_plus_vertex_buffers: u32 = 24;
|
||||
pub const max_bindings_per_bind_group: u32 = 1000;
|
||||
pub const max_dynamic_uniform_buffers_per_pipeline_layout: u32 = 8;
|
||||
pub const max_dynamic_storage_buffers_per_pipeline_layout: u32 = 4;
|
||||
pub const max_sampled_textures_per_shader_stage: u32 = 16;
|
||||
pub const max_samplers_per_shader_stage: u32 = 16;
|
||||
pub const max_storage_buffers_per_shader_stage: u32 = 8;
|
||||
pub const max_storage_textures_per_shader_stage: u32 = 4;
|
||||
pub const max_uniform_buffers_per_shader_stage: u32 = 12;
|
||||
pub const max_uniform_buffer_binding_size: u64 = 65536;
|
||||
pub const max_storage_buffer_binding_size: u64 = 134217728;
|
||||
pub const min_uniform_buffer_offset_alignment: u32 = 256;
|
||||
pub const min_storage_buffer_offset_alignment: u32 = 256;
|
||||
pub const max_vertex_buffers: u32 = 8;
|
||||
pub const max_buffer_size: u64 = 268435456;
|
||||
pub const max_vertex_attributes: u32 = 16;
|
||||
pub const max_vertex_buffer_array_stride: u32 = 2048;
|
||||
pub const max_inter_stage_shader_components: u32 = 60;
|
||||
pub const max_inter_stage_shader_variables: u32 = 16;
|
||||
pub const max_color_attachments: u32 = 8;
|
||||
pub const max_color_attachment_bytes_per_sample: u32 = 32;
|
||||
pub const max_compute_workgroup_storage_size: u32 = 16384;
|
||||
pub const max_compute_invocations_per_workgroup: u32 = 256;
|
||||
pub const max_compute_workgroup_size_x: u32 = 256;
|
||||
pub const max_compute_workgroup_size_y: u32 = 256;
|
||||
pub const max_compute_workgroup_size_z: u32 = 64;
|
||||
pub const max_compute_workgroups_per_dimension: u32 = 65535;
|
||||
|
||||
pub const max_buffers_per_shader_stage = max_storage_buffers_per_shader_stage + max_uniform_buffers_per_shader_stage;
|
||||
1373
src/sysgpu/main.zig
Normal file
1373
src/sysgpu/main.zig
Normal file
File diff suppressed because it is too large
Load diff
2221
src/sysgpu/metal.zig
Normal file
2221
src/sysgpu/metal.zig
Normal file
File diff suppressed because it is too large
Load diff
382
src/sysgpu/metal/conv.zig
Normal file
382
src/sysgpu/metal/conv.zig
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
const mtl = @import("objc").metal.mtl;
|
||||
const sysgpu = @import("../sysgpu/main.zig");
|
||||
|
||||
pub fn metalBlendFactor(factor: sysgpu.BlendFactor, color: bool) mtl.BlendFactor {
|
||||
return switch (factor) {
|
||||
.zero => mtl.BlendFactorZero,
|
||||
.one => mtl.BlendFactorOne,
|
||||
.src => mtl.BlendFactorSourceColor,
|
||||
.one_minus_src => mtl.BlendFactorOneMinusSourceColor,
|
||||
.src_alpha => mtl.BlendFactorSourceAlpha,
|
||||
.one_minus_src_alpha => mtl.BlendFactorOneMinusSourceAlpha,
|
||||
.dst => mtl.BlendFactorDestinationColor,
|
||||
.one_minus_dst => mtl.BlendFactorOneMinusDestinationColor,
|
||||
.dst_alpha => mtl.BlendFactorDestinationAlpha,
|
||||
.one_minus_dst_alpha => mtl.BlendFactorOneMinusDestinationAlpha,
|
||||
.src_alpha_saturated => mtl.BlendFactorSourceAlphaSaturated,
|
||||
.constant => if (color) mtl.BlendFactorBlendColor else mtl.BlendFactorBlendAlpha,
|
||||
.one_minus_constant => if (color) mtl.BlendFactorOneMinusBlendColor else mtl.BlendFactorOneMinusBlendAlpha,
|
||||
.src1 => mtl.BlendFactorSource1Color,
|
||||
.one_minus_src1 => mtl.BlendFactorOneMinusSource1Color,
|
||||
.src1_alpha => mtl.BlendFactorSource1Alpha,
|
||||
.one_minus_src1_alpha => mtl.BlendFactorOneMinusSource1Alpha,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalBlendOperation(op: sysgpu.BlendOperation) mtl.BlendOperation {
|
||||
return switch (op) {
|
||||
.add => mtl.BlendOperationAdd,
|
||||
.subtract => mtl.BlendOperationSubtract,
|
||||
.reverse_subtract => mtl.BlendOperationReverseSubtract,
|
||||
.min => mtl.BlendOperationMin,
|
||||
.max => mtl.BlendOperationMax,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalColorWriteMask(mask: sysgpu.ColorWriteMaskFlags) mtl.ColorWriteMask {
|
||||
var writeMask = mtl.ColorWriteMaskNone;
|
||||
if (mask.red)
|
||||
writeMask |= mtl.ColorWriteMaskRed;
|
||||
if (mask.green)
|
||||
writeMask |= mtl.ColorWriteMaskGreen;
|
||||
if (mask.blue)
|
||||
writeMask |= mtl.ColorWriteMaskBlue;
|
||||
if (mask.alpha)
|
||||
writeMask |= mtl.ColorWriteMaskAlpha;
|
||||
return writeMask;
|
||||
}
|
||||
|
||||
pub fn metalCommonCounter(name: sysgpu.PipelineStatisticName) mtl.CommonCounter {
|
||||
return switch (name) {
|
||||
.vertex_shader_invocations => mtl.CommonCounterVertexInvocations,
|
||||
.cliiper_invocations => mtl.CommonCounterClipperInvocations,
|
||||
.clipper_primitives_out => mtl.CommonCounterClipperPrimitivesOut,
|
||||
.fragment_shader_invocations => mtl.CommonCounterFragmentInvocations,
|
||||
.compute_shader_invocations => mtl.CommonCounterComputeKernelInvocations,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalCompareFunction(func: sysgpu.CompareFunction) mtl.CompareFunction {
|
||||
return switch (func) {
|
||||
.undefined => unreachable,
|
||||
.never => mtl.CompareFunctionNever,
|
||||
.less => mtl.CompareFunctionLess,
|
||||
.less_equal => mtl.CompareFunctionLessEqual,
|
||||
.greater => mtl.CompareFunctionGreater,
|
||||
.greater_equal => mtl.CompareFunctionGreaterEqual,
|
||||
.equal => mtl.CompareFunctionEqual,
|
||||
.not_equal => mtl.CompareFunctionNotEqual,
|
||||
.always => mtl.CompareFunctionAlways,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalCullMode(mode: sysgpu.CullMode) mtl.CullMode {
|
||||
return switch (mode) {
|
||||
.none => mtl.CullModeNone,
|
||||
.front => mtl.CullModeFront,
|
||||
.back => mtl.CullModeBack,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalIndexType(format: sysgpu.IndexFormat) mtl.IndexType {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => mtl.IndexTypeUInt16,
|
||||
.uint32 => mtl.IndexTypeUInt32,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalIndexElementSize(format: sysgpu.IndexFormat) usize {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => 2,
|
||||
.uint32 => 4,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalLoadAction(op: sysgpu.LoadOp) mtl.LoadAction {
|
||||
return switch (op) {
|
||||
.undefined => unreachable,
|
||||
.load => mtl.LoadActionLoad,
|
||||
.clear => mtl.LoadActionClear,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalPixelFormat(format: sysgpu.Texture.Format) mtl.PixelFormat {
|
||||
return switch (format) {
|
||||
.undefined => mtl.PixelFormatInvalid,
|
||||
.r8_unorm => mtl.PixelFormatR8Unorm,
|
||||
.r8_snorm => mtl.PixelFormatR8Snorm,
|
||||
.r8_uint => mtl.PixelFormatR8Uint,
|
||||
.r8_sint => mtl.PixelFormatR8Sint,
|
||||
.r16_uint => mtl.PixelFormatR16Uint,
|
||||
.r16_sint => mtl.PixelFormatR16Sint,
|
||||
.r16_float => mtl.PixelFormatR16Float,
|
||||
.rg8_unorm => mtl.PixelFormatRG8Unorm,
|
||||
.rg8_snorm => mtl.PixelFormatRG8Snorm,
|
||||
.rg8_uint => mtl.PixelFormatRG8Uint,
|
||||
.rg8_sint => mtl.PixelFormatRG8Sint,
|
||||
.r32_float => mtl.PixelFormatR32Float,
|
||||
.r32_uint => mtl.PixelFormatR32Uint,
|
||||
.r32_sint => mtl.PixelFormatR32Sint,
|
||||
.rg16_uint => mtl.PixelFormatRG16Uint,
|
||||
.rg16_sint => mtl.PixelFormatRG16Sint,
|
||||
.rg16_float => mtl.PixelFormatRG16Float,
|
||||
.rgba8_unorm => mtl.PixelFormatRGBA8Unorm,
|
||||
.rgba8_unorm_srgb => mtl.PixelFormatRGBA8Unorm_sRGB,
|
||||
.rgba8_snorm => mtl.PixelFormatRGBA8Snorm,
|
||||
.rgba8_uint => mtl.PixelFormatRGBA8Uint,
|
||||
.rgba8_sint => mtl.PixelFormatRGBA8Sint,
|
||||
.bgra8_unorm => mtl.PixelFormatBGRA8Unorm,
|
||||
.bgra8_unorm_srgb => mtl.PixelFormatBGRA8Unorm_sRGB,
|
||||
.rgb10_a2_unorm => mtl.PixelFormatRGB10A2Unorm,
|
||||
.rg11_b10_ufloat => mtl.PixelFormatRG11B10Float,
|
||||
.rgb9_e5_ufloat => mtl.PixelFormatRGB9E5Float,
|
||||
.rg32_float => mtl.PixelFormatRG32Float,
|
||||
.rg32_uint => mtl.PixelFormatRG32Uint,
|
||||
.rg32_sint => mtl.PixelFormatRG32Sint,
|
||||
.rgba16_uint => mtl.PixelFormatRGBA16Uint,
|
||||
.rgba16_sint => mtl.PixelFormatRGBA16Sint,
|
||||
.rgba16_float => mtl.PixelFormatRGBA16Float,
|
||||
.rgba32_float => mtl.PixelFormatRGBA32Float,
|
||||
.rgba32_uint => mtl.PixelFormatRGBA32Uint,
|
||||
.rgba32_sint => mtl.PixelFormatRGBA32Sint,
|
||||
.stencil8 => mtl.PixelFormatStencil8,
|
||||
.depth16_unorm => mtl.PixelFormatDepth16Unorm,
|
||||
.depth24_plus => mtl.PixelFormatDepth32Float, // mtl.PixelFormatDepth24Unorm_Stencil8 only for non-Apple Silicon
|
||||
.depth24_plus_stencil8 => mtl.PixelFormatDepth32Float_Stencil8, // mtl.PixelFormatDepth24Unorm_Stencil8 only for non-Apple Silicon
|
||||
.depth32_float => mtl.PixelFormatDepth32Float,
|
||||
.depth32_float_stencil8 => mtl.PixelFormatDepth32Float_Stencil8,
|
||||
.bc1_rgba_unorm => mtl.PixelFormatBC1_RGBA,
|
||||
.bc1_rgba_unorm_srgb => mtl.PixelFormatBC1_RGBA_sRGB,
|
||||
.bc2_rgba_unorm => mtl.PixelFormatBC2_RGBA,
|
||||
.bc2_rgba_unorm_srgb => mtl.PixelFormatBC2_RGBA_sRGB,
|
||||
.bc3_rgba_unorm => mtl.PixelFormatBC3_RGBA,
|
||||
.bc3_rgba_unorm_srgb => mtl.PixelFormatBC3_RGBA_sRGB,
|
||||
.bc4_runorm => mtl.PixelFormatBC4_RUnorm,
|
||||
.bc4_rsnorm => mtl.PixelFormatBC4_RSnorm,
|
||||
.bc5_rg_unorm => mtl.PixelFormatBC5_RGUnorm,
|
||||
.bc5_rg_snorm => mtl.PixelFormatBC5_RGSnorm,
|
||||
.bc6_hrgb_ufloat => mtl.PixelFormatBC6H_RGBUfloat,
|
||||
.bc6_hrgb_float => mtl.PixelFormatBC6H_RGBFloat,
|
||||
.bc7_rgba_unorm => mtl.PixelFormatBC7_RGBAUnorm,
|
||||
.bc7_rgba_unorm_srgb => mtl.PixelFormatBC7_RGBAUnorm_sRGB,
|
||||
.etc2_rgb8_unorm => mtl.PixelFormatETC2_RGB8,
|
||||
.etc2_rgb8_unorm_srgb => mtl.PixelFormatETC2_RGB8_sRGB,
|
||||
.etc2_rgb8_a1_unorm => mtl.PixelFormatETC2_RGB8A1,
|
||||
.etc2_rgb8_a1_unorm_srgb => mtl.PixelFormatETC2_RGB8A1_sRGB,
|
||||
.etc2_rgba8_unorm => mtl.PixelFormatEAC_RGBA8,
|
||||
.etc2_rgba8_unorm_srgb => mtl.PixelFormatEAC_RGBA8_sRGB,
|
||||
.eacr11_unorm => mtl.PixelFormatEAC_R11Unorm,
|
||||
.eacr11_snorm => mtl.PixelFormatEAC_R11Snorm,
|
||||
.eacrg11_unorm => mtl.PixelFormatEAC_RG11Unorm,
|
||||
.eacrg11_snorm => mtl.PixelFormatEAC_RG11Snorm,
|
||||
.astc4x4_unorm => mtl.PixelFormatASTC_4x4_LDR,
|
||||
.astc4x4_unorm_srgb => mtl.PixelFormatASTC_4x4_sRGB,
|
||||
.astc5x4_unorm => mtl.PixelFormatASTC_5x4_LDR,
|
||||
.astc5x4_unorm_srgb => mtl.PixelFormatASTC_5x4_sRGB,
|
||||
.astc5x5_unorm => mtl.PixelFormatASTC_5x5_LDR,
|
||||
.astc5x5_unorm_srgb => mtl.PixelFormatASTC_5x5_sRGB,
|
||||
.astc6x5_unorm => mtl.PixelFormatASTC_6x5_LDR,
|
||||
.astc6x5_unorm_srgb => mtl.PixelFormatASTC_6x5_sRGB,
|
||||
.astc6x6_unorm => mtl.PixelFormatASTC_6x6_LDR,
|
||||
.astc6x6_unorm_srgb => mtl.PixelFormatASTC_6x6_sRGB,
|
||||
.astc8x5_unorm => mtl.PixelFormatASTC_8x5_LDR,
|
||||
.astc8x5_unorm_srgb => mtl.PixelFormatASTC_8x5_sRGB,
|
||||
.astc8x6_unorm => mtl.PixelFormatASTC_8x6_LDR,
|
||||
.astc8x6_unorm_srgb => mtl.PixelFormatASTC_8x6_sRGB,
|
||||
.astc8x8_unorm => mtl.PixelFormatASTC_8x8_LDR,
|
||||
.astc8x8_unorm_srgb => mtl.PixelFormatASTC_8x8_sRGB,
|
||||
.astc10x5_unorm => mtl.PixelFormatASTC_10x5_LDR,
|
||||
.astc10x5_unorm_srgb => mtl.PixelFormatASTC_10x5_sRGB,
|
||||
.astc10x6_unorm => mtl.PixelFormatASTC_10x6_LDR,
|
||||
.astc10x6_unorm_srgb => mtl.PixelFormatASTC_10x6_sRGB,
|
||||
.astc10x8_unorm => mtl.PixelFormatASTC_10x8_LDR,
|
||||
.astc10x8_unorm_srgb => mtl.PixelFormatASTC_10x8_sRGB,
|
||||
.astc10x10_unorm => mtl.PixelFormatASTC_10x10_LDR,
|
||||
.astc10x10_unorm_srgb => mtl.PixelFormatASTC_10x10_sRGB,
|
||||
.astc12x10_unorm => mtl.PixelFormatASTC_12x10_LDR,
|
||||
.astc12x10_unorm_srgb => mtl.PixelFormatASTC_12x10_sRGB,
|
||||
.astc12x12_unorm => mtl.PixelFormatASTC_12x12_LDR,
|
||||
.astc12x12_unorm_srgb => mtl.PixelFormatASTC_12x12_sRGB,
|
||||
.r8_bg8_biplanar420_unorm => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalPixelFormatForView(viewFormat: sysgpu.Texture.Format, textureFormat: mtl.PixelFormat, aspect: sysgpu.Texture.Aspect) mtl.PixelFormat {
|
||||
// TODO - depth/stencil only views
|
||||
_ = aspect;
|
||||
_ = textureFormat;
|
||||
|
||||
return metalPixelFormat(viewFormat);
|
||||
}
|
||||
|
||||
pub fn metalPrimitiveTopologyClass(topology: sysgpu.PrimitiveTopology) mtl.PrimitiveTopologyClass {
|
||||
return switch (topology) {
|
||||
.point_list => mtl.PrimitiveTopologyClassPoint,
|
||||
.line_list => mtl.PrimitiveTopologyClassLine,
|
||||
.line_strip => mtl.PrimitiveTopologyClassLine,
|
||||
.triangle_list => mtl.PrimitiveTopologyClassTriangle,
|
||||
.triangle_strip => mtl.PrimitiveTopologyClassTriangle,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalPrimitiveType(topology: sysgpu.PrimitiveTopology) mtl.PrimitiveType {
|
||||
return switch (topology) {
|
||||
.point_list => mtl.PrimitiveTypePoint,
|
||||
.line_list => mtl.PrimitiveTypeLine,
|
||||
.line_strip => mtl.PrimitiveTypeLineStrip,
|
||||
.triangle_list => mtl.PrimitiveTypeTriangle,
|
||||
.triangle_strip => mtl.PrimitiveTypeTriangleStrip,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalResourceOptionsForBuffer(usage: sysgpu.Buffer.UsageFlags) mtl.ResourceOptions {
|
||||
const cpu_cache_mode = if (usage.map_write and !usage.map_read) mtl.ResourceCPUCacheModeWriteCombined else mtl.ResourceCPUCacheModeDefaultCache;
|
||||
const storage_mode = mtl.ResourceStorageModeShared; // optimizing for UMA only
|
||||
const hazard_tracking_mode = mtl.ResourceHazardTrackingModeDefault;
|
||||
return cpu_cache_mode | storage_mode | hazard_tracking_mode;
|
||||
}
|
||||
|
||||
pub fn metalSamplerAddressMode(mode: sysgpu.Sampler.AddressMode) mtl.SamplerAddressMode {
|
||||
return switch (mode) {
|
||||
.repeat => mtl.SamplerAddressModeRepeat,
|
||||
.mirror_repeat => mtl.SamplerAddressModeMirrorRepeat,
|
||||
.clamp_to_edge => mtl.SamplerAddressModeClampToEdge,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalSamplerMinMagFilter(mode: sysgpu.FilterMode) mtl.SamplerMinMagFilter {
|
||||
return switch (mode) {
|
||||
.nearest => mtl.SamplerMinMagFilterNearest,
|
||||
.linear => mtl.SamplerMinMagFilterLinear,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalSamplerMipFilter(mode: sysgpu.MipmapFilterMode) mtl.SamplerMipFilter {
|
||||
return switch (mode) {
|
||||
.nearest => mtl.SamplerMipFilterNearest,
|
||||
.linear => mtl.SamplerMipFilterLinear,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalStencilOperation(op: sysgpu.StencilOperation) mtl.StencilOperation {
|
||||
return switch (op) {
|
||||
.keep => mtl.StencilOperationKeep,
|
||||
.zero => mtl.StencilOperationZero,
|
||||
.replace => mtl.StencilOperationReplace,
|
||||
.invert => mtl.StencilOperationInvert,
|
||||
.increment_clamp => mtl.StencilOperationIncrementClamp,
|
||||
.decrement_clamp => mtl.StencilOperationDecrementClamp,
|
||||
.increment_wrap => mtl.StencilOperationIncrementWrap,
|
||||
.decrement_wrap => mtl.StencilOperationDecrementWrap,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalStorageModeForTexture(usage: sysgpu.Texture.UsageFlags) mtl.StorageMode {
|
||||
if (usage.transient_attachment) {
|
||||
return mtl.StorageModeMemoryless;
|
||||
} else {
|
||||
return mtl.StorageModePrivate;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn metalStoreAction(op: sysgpu.StoreOp, has_resolve_target: bool) mtl.StoreAction {
|
||||
return switch (op) {
|
||||
.undefined => unreachable,
|
||||
.store => if (has_resolve_target) mtl.StoreActionStoreAndMultisampleResolve else mtl.StoreActionStore,
|
||||
.discard => if (has_resolve_target) mtl.StoreActionMultisampleResolve else mtl.StoreActionDontCare,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalTextureType(dimension: sysgpu.Texture.Dimension, size: sysgpu.Extent3D, sample_count: u32) mtl.TextureType {
|
||||
return switch (dimension) {
|
||||
.dimension_1d => if (size.depth_or_array_layers > 1) mtl.TextureType1DArray else mtl.TextureType1D,
|
||||
.dimension_2d => if (sample_count > 1)
|
||||
if (size.depth_or_array_layers > 1)
|
||||
mtl.TextureType2DMultisampleArray
|
||||
else
|
||||
mtl.TextureType2DMultisample
|
||||
else if (size.depth_or_array_layers > 1)
|
||||
mtl.TextureType2DArray
|
||||
else
|
||||
mtl.TextureType2D,
|
||||
.dimension_3d => mtl.TextureType3D,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalTextureTypeForView(dimension: sysgpu.TextureView.Dimension) mtl.TextureType {
|
||||
return switch (dimension) {
|
||||
.dimension_undefined => unreachable,
|
||||
.dimension_1d => mtl.TextureType1D,
|
||||
.dimension_2d => mtl.TextureType2D,
|
||||
.dimension_2d_array => mtl.TextureType2DArray,
|
||||
.dimension_cube => mtl.TextureTypeCube,
|
||||
.dimension_cube_array => mtl.TextureTypeCubeArray,
|
||||
.dimension_3d => mtl.TextureType3D,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalTextureUsage(usage: sysgpu.Texture.UsageFlags, view_format_count: usize) mtl.TextureUsage {
|
||||
var mtl_usage = mtl.TextureUsageUnknown;
|
||||
if (usage.texture_binding)
|
||||
mtl_usage |= mtl.TextureUsageShaderRead;
|
||||
if (usage.storage_binding)
|
||||
mtl_usage |= mtl.TextureUsageShaderWrite;
|
||||
if (usage.render_attachment)
|
||||
mtl_usage |= mtl.TextureUsageRenderTarget;
|
||||
if (view_format_count > 0)
|
||||
mtl_usage |= mtl.TextureUsagePixelFormatView;
|
||||
return mtl_usage;
|
||||
}
|
||||
|
||||
pub fn metalVertexFormat(format: sysgpu.VertexFormat) mtl.VertexFormat {
|
||||
return switch (format) {
|
||||
.undefined => mtl.VertexFormatInvalid,
|
||||
.uint8x2 => mtl.VertexFormatUChar2,
|
||||
.uint8x4 => mtl.VertexFormatUChar4,
|
||||
.sint8x2 => mtl.VertexFormatChar2,
|
||||
.sint8x4 => mtl.VertexFormatChar4,
|
||||
.unorm8x2 => mtl.VertexFormatUChar2Normalized,
|
||||
.unorm8x4 => mtl.VertexFormatUChar4Normalized,
|
||||
.snorm8x2 => mtl.VertexFormatChar2Normalized,
|
||||
.snorm8x4 => mtl.VertexFormatChar4Normalized,
|
||||
.uint16x2 => mtl.VertexFormatUShort2,
|
||||
.uint16x4 => mtl.VertexFormatUShort4,
|
||||
.sint16x2 => mtl.VertexFormatShort2,
|
||||
.sint16x4 => mtl.VertexFormatShort4,
|
||||
.unorm16x2 => mtl.VertexFormatUShort2Normalized,
|
||||
.unorm16x4 => mtl.VertexFormatUShort4Normalized,
|
||||
.snorm16x2 => mtl.VertexFormatShort2Normalized,
|
||||
.snorm16x4 => mtl.VertexFormatShort4Normalized,
|
||||
.float16x2 => mtl.VertexFormatHalf2,
|
||||
.float16x4 => mtl.VertexFormatHalf4,
|
||||
.float32 => mtl.VertexFormatFloat,
|
||||
.float32x2 => mtl.VertexFormatFloat2,
|
||||
.float32x3 => mtl.VertexFormatFloat3,
|
||||
.float32x4 => mtl.VertexFormatFloat4,
|
||||
.uint32 => mtl.VertexFormatUInt,
|
||||
.uint32x2 => mtl.VertexFormatUInt2,
|
||||
.uint32x3 => mtl.VertexFormatUInt3,
|
||||
.uint32x4 => mtl.VertexFormatUInt4,
|
||||
.sint32 => mtl.VertexFormatInt,
|
||||
.sint32x2 => mtl.VertexFormatInt2,
|
||||
.sint32x3 => mtl.VertexFormatInt3,
|
||||
.sint32x4 => mtl.VertexFormatInt4,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalVertexStepFunction(mode: sysgpu.VertexStepMode) mtl.VertexStepFunction {
|
||||
return switch (mode) {
|
||||
.vertex => mtl.VertexStepFunctionPerVertex,
|
||||
.instance => mtl.VertexStepFunctionPerInstance,
|
||||
.vertex_buffer_not_used => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn metalWinding(face: sysgpu.FrontFace) mtl.Winding {
|
||||
return switch (face) {
|
||||
.ccw => mtl.WindingCounterClockwise,
|
||||
.cw => mtl.WindingClockwise,
|
||||
};
|
||||
}
|
||||
2626
src/sysgpu/opengl.zig
Normal file
2626
src/sysgpu/opengl.zig
Normal file
File diff suppressed because it is too large
Load diff
6
src/sysgpu/opengl/c.zig
Normal file
6
src/sysgpu/opengl/c.zig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub usingnamespace @cImport({
|
||||
@cInclude("windows.h");
|
||||
@cInclude("GL/glcorearb.h");
|
||||
@cInclude("GL/glext.h");
|
||||
@cInclude("GL/wglext.h");
|
||||
});
|
||||
263
src/sysgpu/opengl/conv.zig
Normal file
263
src/sysgpu/opengl/conv.zig
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
const sysgpu = @import("../sysgpu/main.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
const c = @import("c.zig");
|
||||
|
||||
fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
|
||||
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
|
||||
}
|
||||
|
||||
pub fn glAttributeCount(format: sysgpu.VertexFormat) c.GLint {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint8x2 => 2,
|
||||
.uint8x4 => 4,
|
||||
.sint8x2 => 2,
|
||||
.sint8x4 => 4,
|
||||
.unorm8x2 => 2,
|
||||
.unorm8x4 => 4,
|
||||
.snorm8x2 => 2,
|
||||
.snorm8x4 => 4,
|
||||
.uint16x2 => 2,
|
||||
.uint16x4 => 4,
|
||||
.sint16x2 => 2,
|
||||
.sint16x4 => 4,
|
||||
.unorm16x2 => 2,
|
||||
.unorm16x4 => 4,
|
||||
.snorm16x2 => 2,
|
||||
.snorm16x4 => 4,
|
||||
.float16x2 => 2,
|
||||
.float16x4 => 4,
|
||||
.float32 => 1,
|
||||
.float32x2 => 2,
|
||||
.float32x3 => 3,
|
||||
.float32x4 => 4,
|
||||
.uint32 => 1,
|
||||
.uint32x2 => 2,
|
||||
.uint32x3 => 3,
|
||||
.uint32x4 => 4,
|
||||
.sint32 => 1,
|
||||
.sint32x2 => 2,
|
||||
.sint32x3 => 3,
|
||||
.sint32x4 => 4,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glAttributeIsNormalized(format_type: utils.FormatType) c.GLboolean {
|
||||
return switch (format_type) {
|
||||
.unorm, .unorm_srgb, .snorm => c.GL_TRUE,
|
||||
else => c.GL_FALSE,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glAttributeIsInt(format_type: utils.FormatType) bool {
|
||||
return switch (format_type) {
|
||||
.uint, .sint => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glAttributeType(format: sysgpu.VertexFormat) c.GLenum {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint8x2 => c.GL_UNSIGNED_BYTE,
|
||||
.uint8x4 => c.GL_UNSIGNED_BYTE,
|
||||
.sint8x2 => c.GL_BYTE,
|
||||
.sint8x4 => c.GL_BYTE,
|
||||
.unorm8x2 => c.GL_UNSIGNED_BYTE,
|
||||
.unorm8x4 => c.GL_UNSIGNED_BYTE,
|
||||
.snorm8x2 => c.GL_BYTE,
|
||||
.snorm8x4 => c.GL_BYTE,
|
||||
.uint16x2 => c.GL_UNSIGNED_SHORT,
|
||||
.uint16x4 => c.GL_UNSIGNED_SHORT,
|
||||
.sint16x2 => c.GL_SHORT,
|
||||
.sint16x4 => c.GL_SHORT,
|
||||
.unorm16x2 => c.GL_UNSIGNED_SHORT,
|
||||
.unorm16x4 => c.GL_UNSIGNED_SHORT,
|
||||
.snorm16x2 => c.GL_SHORT,
|
||||
.snorm16x4 => c.GL_SHORT,
|
||||
.float16x2 => c.GL_HALF_FLOAT,
|
||||
.float16x4 => c.GL_HALF_FLOAT,
|
||||
.float32 => c.GL_FLOAT,
|
||||
.float32x2 => c.GL_FLOAT,
|
||||
.float32x3 => c.GL_FLOAT,
|
||||
.float32x4 => c.GL_FLOAT,
|
||||
.uint32 => c.GL_UNSIGNED_INT,
|
||||
.uint32x2 => c.GL_UNSIGNED_INT,
|
||||
.uint32x3 => c.GL_UNSIGNED_INT,
|
||||
.uint32x4 => c.GL_UNSIGNED_INT,
|
||||
.sint32 => c.GL_INT,
|
||||
.sint32x2 => c.GL_INT,
|
||||
.sint32x3 => c.GL_INT,
|
||||
.sint32x4 => c.GL_INT,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glBlendFactor(factor: sysgpu.BlendFactor, color: bool) c.GLenum {
|
||||
return switch (factor) {
|
||||
.zero => c.GL_ZERO,
|
||||
.one => c.GL_ONE,
|
||||
.src => c.GL_SRC_COLOR,
|
||||
.one_minus_src => c.GL_ONE_MINUS_SRC_COLOR,
|
||||
.src_alpha => c.GL_SRC_ALPHA,
|
||||
.one_minus_src_alpha => c.GL_ONE_MINUS_SRC_ALPHA,
|
||||
.dst => c.GL_DST_COLOR,
|
||||
.one_minus_dst => c.GL_ONE_MINUS_DST_COLOR,
|
||||
.dst_alpha => c.GL_DST_ALPHA,
|
||||
.one_minus_dst_alpha => c.GL_ONE_MINUS_DST_ALPHA,
|
||||
.src_alpha_saturated => c.GL_SRC_ALPHA_SATURATE,
|
||||
.constant => if (color) c.GL_CONSTANT_COLOR else c.GL_CONSTANT_ALPHA,
|
||||
.one_minus_constant => if (color) c.GL_ONE_MINUS_CONSTANT_COLOR else c.GL_ONE_MINUS_CONSTANT_ALPHA,
|
||||
.src1 => c.GL_SRC1_COLOR,
|
||||
.one_minus_src1 => c.GL_ONE_MINUS_SRC1_COLOR,
|
||||
.src1_alpha => c.GL_SRC1_ALPHA,
|
||||
.one_minus_src1_alpha => c.GL_ONE_MINUS_SRC1_ALPHA,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glBlendOp(op: sysgpu.BlendOperation) c.GLenum {
|
||||
return switch (op) {
|
||||
.add => c.GL_FUNC_ADD,
|
||||
.subtract => c.GL_FUNC_SUBTRACT,
|
||||
.reverse_subtract => c.GL_FUNC_REVERSE_SUBTRACT,
|
||||
.min => c.GL_MIN,
|
||||
.max => c.GL_MAX,
|
||||
};
|
||||
}
|
||||
|
||||
//pub fn glBufferDataUsage(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLenum {}
|
||||
|
||||
pub fn glBufferStorageFlags(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield {
|
||||
var flags: c.GLbitfield = 0;
|
||||
if (mapped_at_creation == .true)
|
||||
flags |= c.GL_MAP_WRITE_BIT;
|
||||
if (usage.map_read)
|
||||
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT;
|
||||
if (usage.map_write)
|
||||
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_COHERENT_BIT | c.GL_MAP_WRITE_BIT;
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn glCompareFunc(func: sysgpu.CompareFunction) c.GLenum {
|
||||
return switch (func) {
|
||||
.undefined => unreachable,
|
||||
.never => c.GL_NEVER,
|
||||
.less => c.GL_LESS,
|
||||
.less_equal => c.GL_LEQUAL,
|
||||
.greater => c.GL_GREATER,
|
||||
.greater_equal => c.GL_GEQUAL,
|
||||
.equal => c.GL_EQUAL,
|
||||
.not_equal => c.GL_NOTEQUAL,
|
||||
.always => c.GL_ALWAYS,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glCullEnabled(cull_mode: sysgpu.CullMode) bool {
|
||||
return switch (cull_mode) {
|
||||
.none => false,
|
||||
else => true,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glCullFace(cull_mode: sysgpu.CullMode) c.GLenum {
|
||||
return switch (cull_mode) {
|
||||
.none => c.GL_BACK,
|
||||
.front => c.GL_FRONT,
|
||||
.back => c.GL_BACK,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glDepthMask(ds: *const sysgpu.DepthStencilState) c.GLboolean {
|
||||
return if (ds.depth_write_enabled == .true) c.GL_TRUE else c.GL_FALSE;
|
||||
}
|
||||
|
||||
pub fn glDepthTestEnabled(ds: *const sysgpu.DepthStencilState) bool {
|
||||
return ds.depth_compare != .always or ds.depth_write_enabled == .true;
|
||||
}
|
||||
|
||||
pub fn glFrontFace(front_face: sysgpu.FrontFace) c.GLenum {
|
||||
return switch (front_face) {
|
||||
.ccw => c.GL_CCW,
|
||||
.cw => c.GL_CW,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glIndexType(format: sysgpu.IndexFormat) c.GLenum {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => c.GL_UNSIGNED_SHORT,
|
||||
.uint32 => c.GL_UNSIGNED_INT,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glIndexElementSize(format: sysgpu.IndexFormat) usize {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => 2,
|
||||
.uint32 => 4,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glMapAccess(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield {
|
||||
var flags: c.GLbitfield = 0;
|
||||
if (mapped_at_creation == .true)
|
||||
flags |= c.GL_MAP_WRITE_BIT;
|
||||
if (usage.map_read)
|
||||
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT;
|
||||
if (usage.map_write)
|
||||
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_WRITE_BIT;
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn glPrimitiveMode(topology: sysgpu.PrimitiveTopology) c.GLenum {
|
||||
return switch (topology) {
|
||||
.point_list => c.GL_POINTS,
|
||||
.line_list => c.GL_LINES,
|
||||
.line_strip => c.GL_LINE_STRIP,
|
||||
.triangle_list => c.GL_TRIANGLES,
|
||||
.triangle_strip => c.GL_TRIANGLE_STRIP,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glStencilOp(op: sysgpu.StencilOperation) c.GLenum {
|
||||
return switch (op) {
|
||||
.keep => c.GL_KEEP,
|
||||
.zero => c.GL_ZERO,
|
||||
.replace => c.GL_REPLACE,
|
||||
.invert => c.GL_INVERT,
|
||||
.increment_clamp => c.GL_INCR,
|
||||
.decrement_clamp => c.GL_DECR,
|
||||
.increment_wrap => c.GL_INCR_WRAP,
|
||||
.decrement_wrap => c.GL_DECR_WRAP,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glStencilTestEnabled(ds: *const sysgpu.DepthStencilState) bool {
|
||||
return stencilEnable(ds.stencil_front) or stencilEnable(ds.stencil_back);
|
||||
}
|
||||
|
||||
pub fn glTargetForBuffer(usage: sysgpu.Buffer.UsageFlags) c.GLenum {
|
||||
// Not sure if this matters anymore - only get to pick one anyway
|
||||
if (usage.index)
|
||||
return c.GL_ELEMENT_ARRAY_BUFFER;
|
||||
if (usage.vertex)
|
||||
return c.GL_ARRAY_BUFFER;
|
||||
if (usage.uniform)
|
||||
return c.GL_UNIFORM_BUFFER;
|
||||
if (usage.storage)
|
||||
return c.GL_SHADER_STORAGE_BUFFER;
|
||||
if (usage.indirect)
|
||||
return c.GL_DRAW_INDIRECT_BUFFER;
|
||||
if (usage.query_resolve)
|
||||
return c.GL_QUERY_BUFFER;
|
||||
|
||||
return c.GL_ARRAY_BUFFER;
|
||||
}
|
||||
|
||||
pub fn glTargetForBufferBinding(binding_type: sysgpu.Buffer.BindingType) c.GLenum {
|
||||
return switch (binding_type) {
|
||||
.undefined => unreachable,
|
||||
.uniform => c.GL_UNIFORM_BUFFER,
|
||||
.storage => c.GL_SHADER_STORAGE_BUFFER,
|
||||
.read_only_storage => c.GL_SHADER_STORAGE_BUFFER,
|
||||
};
|
||||
}
|
||||
1459
src/sysgpu/opengl/proc.zig
Normal file
1459
src/sysgpu/opengl/proc.zig
Normal file
File diff suppressed because it is too large
Load diff
22
src/sysgpu/shader.zig
Normal file
22
src/sysgpu/shader.zig
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const CodeGen = @import("shader/CodeGen.zig");
|
||||
pub const Air = @import("shader/Air.zig");
|
||||
pub const Ast = @import("shader/Ast.zig");
|
||||
pub const Parser = @import("shader/Parser.zig");
|
||||
pub const Token = @import("shader/Token.zig");
|
||||
pub const Tokenizer = @import("shader/Tokenizer.zig");
|
||||
pub const ErrorList = @import("shader/ErrorList.zig");
|
||||
pub const printAir = @import("shader/print_air.zig").printAir;
|
||||
|
||||
test "reference declarations" {
|
||||
std.testing.refAllDecls(CodeGen);
|
||||
std.testing.refAllDecls(Air);
|
||||
std.testing.refAllDecls(Ast);
|
||||
std.testing.refAllDecls(Parser);
|
||||
std.testing.refAllDecls(Token);
|
||||
std.testing.refAllDecls(Tokenizer);
|
||||
std.testing.refAllDecls(ErrorList);
|
||||
_ = printAir;
|
||||
_ = @import("shader/test.zig");
|
||||
}
|
||||
1044
src/sysgpu/shader/Air.zig
Normal file
1044
src/sysgpu/shader/Air.zig
Normal file
File diff suppressed because it is too large
Load diff
764
src/sysgpu/shader/Ast.zig
Normal file
764
src/sysgpu/shader/Ast.zig
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
const std = @import("std");
|
||||
const Parser = @import("Parser.zig");
|
||||
const Token = @import("Token.zig");
|
||||
const Tokenizer = @import("Tokenizer.zig");
|
||||
const ErrorList = @import("ErrorList.zig");
|
||||
const Extensions = @import("wgsl.zig").Extensions;
|
||||
|
||||
const Ast = @This();
|
||||
|
||||
pub const NodeList = std.MultiArrayList(Node);
|
||||
pub const TokenList = std.MultiArrayList(Token);
|
||||
|
||||
source: []const u8,
|
||||
tokens: TokenList.Slice,
|
||||
nodes: NodeList.Slice,
|
||||
extra: []const u32,
|
||||
extensions: Extensions,
|
||||
|
||||
pub fn deinit(tree: *Ast, allocator: std.mem.Allocator) void {
|
||||
tree.tokens.deinit(allocator);
|
||||
tree.nodes.deinit(allocator);
|
||||
allocator.free(tree.extra);
|
||||
tree.* = undefined;
|
||||
}
|
||||
|
||||
/// parses a TranslationUnit (WGSL Program)
|
||||
pub fn parse(allocator: std.mem.Allocator, errors: *ErrorList, source: [:0]const u8) error{ OutOfMemory, Parsing }!Ast {
|
||||
var p = Parser{
|
||||
.allocator = allocator,
|
||||
.source = source,
|
||||
.tokens = blk: {
|
||||
const estimated_tokens = source.len / 8;
|
||||
|
||||
var tokens = std.MultiArrayList(Token){};
|
||||
errdefer tokens.deinit(allocator);
|
||||
|
||||
try tokens.ensureTotalCapacity(allocator, estimated_tokens);
|
||||
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
try tokens.append(allocator, tok);
|
||||
if (tok.tag == .eof) break;
|
||||
}
|
||||
|
||||
break :blk tokens;
|
||||
},
|
||||
.errors = errors,
|
||||
};
|
||||
defer p.scratch.deinit(allocator);
|
||||
errdefer {
|
||||
p.tokens.deinit(allocator);
|
||||
p.nodes.deinit(allocator);
|
||||
p.extra.deinit(allocator);
|
||||
}
|
||||
|
||||
const estimated_nodes = p.tokens.len / 2 + 1;
|
||||
try p.nodes.ensureTotalCapacity(allocator, estimated_nodes);
|
||||
|
||||
try p.translationUnit();
|
||||
|
||||
return .{
|
||||
.source = source,
|
||||
.tokens = p.tokens.toOwnedSlice(),
|
||||
.nodes = p.nodes.toOwnedSlice(),
|
||||
.extra = try p.extra.toOwnedSlice(allocator),
|
||||
.extensions = p.extensions,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn spanToList(tree: Ast, span: NodeIndex) []const NodeIndex {
|
||||
std.debug.assert(tree.nodeTag(span) == .span);
|
||||
return @ptrCast(tree.extra[@intFromEnum(tree.nodeLHS(span))..@intFromEnum(tree.nodeRHS(span))]);
|
||||
}
|
||||
|
||||
pub fn extraData(tree: Ast, comptime T: type, index: NodeIndex) T {
|
||||
const fields = std.meta.fields(T);
|
||||
var result: T = undefined;
|
||||
inline for (fields, 0..) |field, i| {
|
||||
@field(result, field.name) = @enumFromInt(tree.extra[@intFromEnum(index) + i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn tokenTag(tree: Ast, i: TokenIndex) Token.Tag {
|
||||
return tree.tokens.items(.tag)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn tokenLoc(tree: Ast, i: TokenIndex) Token.Loc {
|
||||
return tree.tokens.items(.loc)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn nodeTag(tree: Ast, i: NodeIndex) Node.Tag {
|
||||
return tree.nodes.items(.tag)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn nodeToken(tree: Ast, i: NodeIndex) TokenIndex {
|
||||
return tree.nodes.items(.main_token)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn nodeLHS(tree: Ast, i: NodeIndex) NodeIndex {
|
||||
return tree.nodes.items(.lhs)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn nodeRHS(tree: Ast, i: NodeIndex) NodeIndex {
|
||||
return tree.nodes.items(.rhs)[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn nodeLoc(tree: Ast, i: NodeIndex) Token.Loc {
|
||||
var loc = tree.tokenLoc(tree.nodeToken(i));
|
||||
switch (tree.nodeTag(i)) {
|
||||
.deref, .addr_of => {
|
||||
const lhs_loc = tree.tokenLoc(tree.nodeToken(tree.nodeLHS(i)));
|
||||
loc.end = lhs_loc.end;
|
||||
},
|
||||
.field_access => {
|
||||
const component_loc = tree.tokenLoc(@enumFromInt(@intFromEnum(tree.nodeToken(i)) + 1));
|
||||
loc.end = component_loc.end;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
pub fn declNameLoc(tree: Ast, node: NodeIndex) ?Token.Loc {
|
||||
const token: TokenIndex = switch (tree.nodeTag(node)) {
|
||||
.global_var => tree.extraData(Node.GlobalVar, tree.nodeLHS(node)).name,
|
||||
.@"var" => tree.extraData(Node.Var, tree.nodeLHS(node)).name,
|
||||
.@"struct",
|
||||
.@"fn",
|
||||
.@"const",
|
||||
.let,
|
||||
.override,
|
||||
.type_alias,
|
||||
=> @enumFromInt(@intFromEnum(tree.nodeToken(node)) + 1),
|
||||
.struct_member, .fn_param => tree.nodeToken(node),
|
||||
else => return null,
|
||||
};
|
||||
return tree.tokenLoc(token);
|
||||
}
|
||||
|
||||
pub const NodeIndex = enum(u32) {
|
||||
none = std.math.maxInt(u32),
|
||||
globals = 0,
|
||||
_,
|
||||
|
||||
pub fn asTokenIndex(self: NodeIndex) TokenIndex {
|
||||
return @enumFromInt(@intFromEnum(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub const TokenIndex = enum(u32) {
|
||||
none = std.math.maxInt(u32),
|
||||
_,
|
||||
|
||||
pub fn asNodeIndex(self: TokenIndex) NodeIndex {
|
||||
return @enumFromInt(@intFromEnum(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Node = struct {
|
||||
tag: Tag,
|
||||
main_token: TokenIndex,
|
||||
lhs: NodeIndex = .none,
|
||||
rhs: NodeIndex = .none,
|
||||
|
||||
pub const Tag = enum {
|
||||
/// an slice NodeIndex in extra [LHS..RHS]
|
||||
/// TOK : undefined
|
||||
/// LHS : NodeIndex
|
||||
/// RHS : NodeIndex
|
||||
span,
|
||||
|
||||
/// TOK : k_var
|
||||
/// LHS : GlobalVar
|
||||
/// RHS : Expr?
|
||||
global_var,
|
||||
|
||||
/// TOK : k_override
|
||||
/// LHS : Override
|
||||
/// RHS : Expr
|
||||
override,
|
||||
|
||||
/// TOK : k_type
|
||||
/// LHS : Type
|
||||
/// RHS : --
|
||||
type_alias,
|
||||
|
||||
/// TOK : k_const_assert
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
const_assert,
|
||||
|
||||
/// TOK : k_struct
|
||||
/// LHS : span(struct_member)
|
||||
/// RHS : --
|
||||
@"struct",
|
||||
/// TOK : ident
|
||||
/// LHS : span(Attribute)
|
||||
/// RHS : Type
|
||||
struct_member,
|
||||
|
||||
/// TOK : k_fn
|
||||
/// LHS : FnProto
|
||||
/// RHS : block
|
||||
@"fn",
|
||||
/// TOK : ident
|
||||
/// LHS : ?span(Attribute)
|
||||
/// RHS : type
|
||||
fn_param,
|
||||
|
||||
/// TOK : brace_left
|
||||
/// LHS : span(Statement)?
|
||||
/// RHS : --
|
||||
block,
|
||||
|
||||
/// TOK : k_return
|
||||
/// LHS : Expr?
|
||||
/// RHS : --
|
||||
@"return",
|
||||
|
||||
/// TOK : k_discard
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
discard,
|
||||
|
||||
/// TOK : k_loop
|
||||
/// LHS : block
|
||||
/// RHS : --
|
||||
loop,
|
||||
|
||||
/// TOK : k_continuing
|
||||
/// LHS : block
|
||||
/// RHS : --
|
||||
continuing,
|
||||
|
||||
/// TOK : k_break
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
break_if,
|
||||
|
||||
/// TOK : k_break
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
@"break",
|
||||
|
||||
/// TOK : k_continue
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
@"continue",
|
||||
|
||||
/// TOK : k_if
|
||||
/// LHS : Expr
|
||||
/// RHS : block
|
||||
@"if",
|
||||
/// RHS is else body
|
||||
/// TOK : k_if
|
||||
/// LHS : if
|
||||
/// RHS : block
|
||||
if_else,
|
||||
/// TOK : k_if
|
||||
/// LHS : if
|
||||
/// RHS : if, if_else, if_else_if
|
||||
if_else_if,
|
||||
|
||||
/// TOK : k_switch
|
||||
/// LHS : Expr
|
||||
/// RHS : span(switch_case, switch_default, switch_case_default)
|
||||
@"switch",
|
||||
/// TOK : k_case
|
||||
/// LHS : span(Expr)
|
||||
/// RHS : block
|
||||
switch_case,
|
||||
/// TOK : k_default
|
||||
/// LHS : --
|
||||
/// RHS : block
|
||||
switch_default,
|
||||
/// switch_case with default (`case 1, 2, default {}`)
|
||||
/// TOK : k_case
|
||||
/// LHS : span(Expr)
|
||||
/// RHS : block
|
||||
switch_case_default,
|
||||
|
||||
/// TOK : k_var
|
||||
/// LHS : Var
|
||||
/// RHS : Expr?
|
||||
@"var",
|
||||
|
||||
/// TOK : k_const
|
||||
/// LHS : Type?
|
||||
/// RHS : Expr
|
||||
@"const",
|
||||
|
||||
/// TOK : k_let
|
||||
/// LHS : Type?
|
||||
/// RHS : Expr
|
||||
let,
|
||||
|
||||
/// TOK : k_while
|
||||
/// LHS : Expr
|
||||
/// RHS : block
|
||||
@"while",
|
||||
|
||||
/// TOK : k_for
|
||||
/// LHS : ForHeader
|
||||
/// RHS : block
|
||||
@"for",
|
||||
|
||||
/// TOK : plus_plus
|
||||
/// LHS : Expr
|
||||
increase,
|
||||
|
||||
/// TOK : minus_minus
|
||||
/// LHS : Expr
|
||||
decrease,
|
||||
|
||||
/// TOK : plus_equal, minus_equal,
|
||||
/// times_equal, division_equal,
|
||||
/// modulo_equal, and_equal,
|
||||
/// or_equal, xor_equal,
|
||||
/// shl_equal, shl_equal
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
compound_assign,
|
||||
|
||||
/// TOK : equal
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
phony_assign,
|
||||
|
||||
/// TOK : k_i32, k_u32, k_f32, k_f16, k_bool
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
number_type,
|
||||
|
||||
/// TOK : k_bool
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
bool_type,
|
||||
|
||||
/// TOK : k_sampler, k_sampler_comparison
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
sampler_type,
|
||||
|
||||
/// TOK : k_vec2, k_vec3, k_vec4
|
||||
/// LHS : Type?
|
||||
/// RHS : --
|
||||
vector_type,
|
||||
|
||||
/// TOK : k_mat2x2, k_mat2x3, k_mat2x4,
|
||||
/// k_mat3x2, k_mat3x3, k_mat3x4,
|
||||
/// k_mat4x2, k_mat4x3, k_mat4x4
|
||||
/// LHS : Type?
|
||||
/// RHS : --
|
||||
matrix_type,
|
||||
|
||||
/// TOK : k_atomic
|
||||
/// LHS : Type
|
||||
/// RHS : --
|
||||
atomic_type,
|
||||
|
||||
/// TOK : k_array
|
||||
/// LHS : Type?
|
||||
/// RHS : Expr?
|
||||
array_type,
|
||||
|
||||
/// TOK : k_ptr
|
||||
/// LHS : Type
|
||||
/// RHS : PtrType
|
||||
ptr_type,
|
||||
|
||||
/// TOK : k_texture_1d, k_texture_2d, k_texture_2d_array,
|
||||
/// k_texture_3d, k_texture_cube, k_texture_cube_array
|
||||
/// LHS : Type
|
||||
/// RHS : --
|
||||
sampled_texture_type,
|
||||
|
||||
/// TOK : k_texture_multisampled_2d, k_texture_depth_multisampled_2d
|
||||
/// LHS : Type?
|
||||
/// RHS : --
|
||||
multisampled_texture_type,
|
||||
|
||||
/// TOK : k_texture_external
|
||||
/// LHS : Type
|
||||
/// RHS : --
|
||||
external_texture_type,
|
||||
|
||||
/// TOK : k_texture_storage_1d, k_texture_storage_2d,
|
||||
/// k_texture_storage_2d_array, k_texture_storage_3d
|
||||
/// LHS : Token(TexelFormat)
|
||||
/// RHS : Token(AccessMode)
|
||||
storage_texture_type,
|
||||
|
||||
/// TOK : k_texture_depth_2d, k_texture_depth_2d_array
|
||||
/// k_texture_depth_cube, k_texture_depth_cube_array
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
depth_texture_type,
|
||||
|
||||
/// TOK : attr
|
||||
attr_const,
|
||||
|
||||
/// TOK : attr
|
||||
attr_invariant,
|
||||
|
||||
/// TOK : attr
|
||||
attr_must_use,
|
||||
|
||||
/// TOK : attr
|
||||
attr_vertex,
|
||||
|
||||
/// TOK : attr
|
||||
attr_fragment,
|
||||
|
||||
/// TOK : attr
|
||||
attr_compute,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_align,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_binding,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_group,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_id,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_location,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
attr_size,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Token(Builtin)
|
||||
/// RHS : --
|
||||
attr_builtin,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : WorkgroupSize
|
||||
/// RHS : --
|
||||
attr_workgroup_size,
|
||||
|
||||
/// TOK : attr
|
||||
/// LHS : Token(InterpolationType)
|
||||
/// RHS : Token(InterpolationSample))
|
||||
attr_interpolate,
|
||||
|
||||
/// TOK : *
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
mul,
|
||||
|
||||
/// TOK : /
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
div,
|
||||
|
||||
/// TOK : %
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
mod,
|
||||
|
||||
/// TOK : +
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
add,
|
||||
|
||||
/// TOK : -
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
sub,
|
||||
|
||||
/// TOK : <<
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
shl,
|
||||
|
||||
/// TOK : >>
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
shr,
|
||||
|
||||
/// TOK : &
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
@"and",
|
||||
|
||||
/// TOK : |
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
@"or",
|
||||
|
||||
/// TOK : ^
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
xor,
|
||||
|
||||
/// TOK : &&
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
logical_and,
|
||||
|
||||
/// TOK : ||
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
logical_or,
|
||||
|
||||
/// TOK : !
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
not,
|
||||
|
||||
/// TOK : -
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
negate,
|
||||
|
||||
/// TOK : *
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
deref,
|
||||
|
||||
/// TOK : &
|
||||
/// LHS : Expr
|
||||
/// RHS : --
|
||||
addr_of,
|
||||
|
||||
/// TOK : ==
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
equal,
|
||||
|
||||
/// TOK : !=
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
not_equal,
|
||||
|
||||
/// TOK : <
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
less_than,
|
||||
|
||||
/// TOK : <=
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
less_than_equal,
|
||||
|
||||
/// TOK : >
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
greater_than,
|
||||
|
||||
/// TOK : >=
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
greater_than_equal,
|
||||
|
||||
/// for identifier, array without element type specified,
|
||||
/// vector prefix (e.g. vec2) and matrix prefix (e.g. mat2x2) RHS is null
|
||||
/// see callExpr in Parser.zig if you don't understand this
|
||||
///
|
||||
/// TOK : ident, k_array, k_bool, 'number type keywords', 'vector keywords', 'matrix keywords'
|
||||
/// LHS : span(Arguments Expr)
|
||||
/// RHS : (number_type, bool_type, vector_type, matrix_type, array_type)?
|
||||
call,
|
||||
|
||||
/// TOK : k_bitcast
|
||||
/// LHS : Type
|
||||
/// RHS : Expr
|
||||
bitcast,
|
||||
|
||||
/// TOK : ident
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
ident,
|
||||
|
||||
/// LHS is prefix expression
|
||||
/// TOK : ident
|
||||
/// LHS : Expr
|
||||
/// RHS : Token(NodeIndex(ident))
|
||||
field_access,
|
||||
|
||||
/// LHS is prefix expression
|
||||
/// TOK : bracket_left
|
||||
/// LHS : Expr
|
||||
/// RHS : Expr
|
||||
index_access,
|
||||
|
||||
/// TOK : k_true
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
true,
|
||||
/// TOK : k_false
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
false,
|
||||
/// TOK : number
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
number,
|
||||
};
|
||||
|
||||
pub const GlobalVar = struct {
|
||||
/// span(Attr)?
|
||||
attrs: NodeIndex = .none,
|
||||
/// Token(ident)
|
||||
name: TokenIndex,
|
||||
/// Token(AddressSpace)?
|
||||
addr_space: TokenIndex = .none,
|
||||
/// Token(AccessMode)?
|
||||
access_mode: TokenIndex = .none,
|
||||
/// Type?
|
||||
type: NodeIndex = .none,
|
||||
};
|
||||
|
||||
pub const Var = struct {
|
||||
/// Token(ident)
|
||||
name: TokenIndex,
|
||||
/// Token(AddressSpace)?
|
||||
addr_space: TokenIndex = .none,
|
||||
/// Token(AccessMode)?
|
||||
access_mode: TokenIndex = .none,
|
||||
/// Type?
|
||||
type: NodeIndex = .none,
|
||||
};
|
||||
|
||||
pub const Override = struct {
|
||||
/// span(Attr)?
|
||||
attrs: NodeIndex = .none,
|
||||
/// Type?
|
||||
type: NodeIndex = .none,
|
||||
};
|
||||
|
||||
pub const PtrType = struct {
|
||||
/// Token(AddressSpace)
|
||||
addr_space: TokenIndex,
|
||||
/// Token(AccessMode)
|
||||
access_mode: TokenIndex,
|
||||
};
|
||||
|
||||
pub const WorkgroupSize = struct {
|
||||
/// Expr
|
||||
x: NodeIndex,
|
||||
/// Expr?
|
||||
y: NodeIndex = .none,
|
||||
/// Expr?
|
||||
z: NodeIndex = .none,
|
||||
};
|
||||
|
||||
pub const FnProto = struct {
|
||||
/// span(Attr)?
|
||||
attrs: NodeIndex = .none,
|
||||
/// span(fn_param)?
|
||||
params: NodeIndex = .none,
|
||||
/// span(Attr)?
|
||||
return_attrs: NodeIndex = .none,
|
||||
/// Type?
|
||||
return_type: NodeIndex = .none,
|
||||
};
|
||||
|
||||
pub const ForHeader = struct {
|
||||
/// var, const, let, phony_assign, compound_assign
|
||||
init: NodeIndex = .none,
|
||||
/// Expr
|
||||
cond: NodeIndex = .none,
|
||||
/// call, phony_assign, compound_assign
|
||||
update: NodeIndex = .none,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Builtin = enum {
|
||||
vertex_index,
|
||||
instance_index,
|
||||
position,
|
||||
front_facing,
|
||||
frag_depth,
|
||||
local_invocation_id,
|
||||
local_invocation_index,
|
||||
global_invocation_id,
|
||||
workgroup_id,
|
||||
num_workgroups,
|
||||
sample_index,
|
||||
sample_mask,
|
||||
};
|
||||
|
||||
pub const InterpolationType = enum {
|
||||
perspective,
|
||||
linear,
|
||||
flat,
|
||||
};
|
||||
|
||||
pub const InterpolationSample = enum {
|
||||
center,
|
||||
centroid,
|
||||
sample,
|
||||
};
|
||||
|
||||
pub const AddressSpace = enum {
|
||||
function,
|
||||
private,
|
||||
workgroup,
|
||||
uniform,
|
||||
storage,
|
||||
};
|
||||
|
||||
pub const AccessMode = enum {
|
||||
read,
|
||||
write,
|
||||
read_write,
|
||||
};
|
||||
|
||||
pub const Attribute = enum {
|
||||
invariant,
|
||||
@"const",
|
||||
must_use,
|
||||
vertex,
|
||||
fragment,
|
||||
compute,
|
||||
@"align",
|
||||
binding,
|
||||
group,
|
||||
id,
|
||||
location,
|
||||
size,
|
||||
builtin,
|
||||
workgroup_size,
|
||||
interpolate,
|
||||
};
|
||||
|
||||
pub const TexelFormat = enum {
|
||||
rgba8unorm,
|
||||
rgba8snorm,
|
||||
rgba8uint,
|
||||
rgba8sint,
|
||||
rgba16uint,
|
||||
rgba16sint,
|
||||
rgba16float,
|
||||
r32uint,
|
||||
r32sint,
|
||||
r32float,
|
||||
rg32uint,
|
||||
rg32sint,
|
||||
rg32float,
|
||||
rgba32uint,
|
||||
rgba32sint,
|
||||
rgba32float,
|
||||
bgra8unorm,
|
||||
};
|
||||
4568
src/sysgpu/shader/AstGen.zig
Normal file
4568
src/sysgpu/shader/AstGen.zig
Normal file
File diff suppressed because it is too large
Load diff
236
src/sysgpu/shader/CodeGen.zig
Normal file
236
src/sysgpu/shader/CodeGen.zig
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
const std = @import("std");
|
||||
const Air = @import("Air.zig");
|
||||
const c = @cImport({
|
||||
@cInclude("spirv-cross/spirv_cross_c.h");
|
||||
@cInclude("spirv-tools/libspirv.h");
|
||||
});
|
||||
const genGlsl = @import("codegen/glsl.zig").gen;
|
||||
const genHlsl = @import("codegen/hlsl.zig").gen;
|
||||
const genMsl = @import("codegen/msl.zig").gen;
|
||||
const genSpirv = @import("codegen/spirv.zig").gen;
|
||||
|
||||
pub const Language = enum {
|
||||
glsl,
|
||||
hlsl,
|
||||
msl,
|
||||
spirv,
|
||||
};
|
||||
|
||||
pub const DebugInfo = struct {
|
||||
emit_source_file: ?[]const u8 = null,
|
||||
emit_names: bool = true,
|
||||
};
|
||||
|
||||
pub const Stage = enum {
|
||||
vertex,
|
||||
fragment,
|
||||
compute,
|
||||
};
|
||||
|
||||
pub const Entrypoint = struct {
|
||||
stage: Stage,
|
||||
name: [*:0]const u8,
|
||||
};
|
||||
|
||||
pub const BindingPoint = struct { group: u32, binding: u32 };
|
||||
pub const BindingTable = std.AutoHashMapUnmanaged(BindingPoint, u32);
|
||||
|
||||
pub fn generate(
|
||||
allocator: std.mem.Allocator,
|
||||
air: *const Air,
|
||||
out_lang: Language,
|
||||
use_spirv_cross: bool,
|
||||
debug_info: DebugInfo,
|
||||
entrypoint: ?Entrypoint,
|
||||
bindings: ?*const BindingTable,
|
||||
label: ?[*:0]const u8,
|
||||
) ![]const u8 {
|
||||
_ = use_spirv_cross;
|
||||
// if (!use_spirv_cross) {
|
||||
// const spirv_data = try genSpirv(allocator, air, .{});
|
||||
// const spirv_data_z = try allocator.dupeZ(u8, spirv_data);
|
||||
// defer allocator.free(spirv_data_z);
|
||||
// allocator.free(spirv_data);
|
||||
|
||||
// const spirv_words_ptr = @as([*]const u32, @ptrCast(@alignCast(spirv_data_z.ptr)));
|
||||
// const spirv_words = spirv_words_ptr[0 .. spirv_data_z.len / @sizeOf(u32)];
|
||||
|
||||
// // Optimize
|
||||
// var optimized_spirv: c.spv_binary = undefined;
|
||||
// const target_env = spvTargetEnv(out_lang);
|
||||
// const optimizer = c.spvOptimizerCreate(target_env);
|
||||
// defer c.spvOptimizerDestroy(optimizer);
|
||||
|
||||
// c.spvOptimizerSetMessageConsumer(optimizer, spvMessageConsumer);
|
||||
// c.spvOptimizerRegisterPerformancePasses(optimizer);
|
||||
// c.spvOptimizerRegisterLegalizationPasses(optimizer);
|
||||
|
||||
// const opt_options = c.spvOptimizerOptionsCreate();
|
||||
// defer c.spvOptimizerOptionsDestroy(opt_options);
|
||||
// c.spvOptimizerOptionsSetRunValidator(opt_options, false);
|
||||
|
||||
// var res = c.spvOptimizerRun(
|
||||
// optimizer,
|
||||
// spirv_words.ptr,
|
||||
// spirv_words.len,
|
||||
// &optimized_spirv,
|
||||
// opt_options,
|
||||
// );
|
||||
// switch (res) {
|
||||
// c.SPV_SUCCESS => {},
|
||||
// else => return error.SpvOptimizerFailed,
|
||||
// }
|
||||
|
||||
// if (out_lang == .spirv) {
|
||||
// const code_bytes_ptr = @as([*]const u8, @ptrCast(optimized_spirv.*.code));
|
||||
// const code_bytes = code_bytes_ptr[0 .. optimized_spirv.*.wordCount * @sizeOf(u32)];
|
||||
// return allocator.dupe(u8, code_bytes);
|
||||
// }
|
||||
|
||||
// // Translate
|
||||
// var context: c.spvc_context = undefined;
|
||||
// _ = c.spvc_context_create(&context);
|
||||
// defer c.spvc_context_destroy(context);
|
||||
// c.spvc_context_set_error_callback(context, spvcErrorCallback, null);
|
||||
|
||||
// var ir: c.spvc_parsed_ir = undefined;
|
||||
// _ = c.spvc_context_parse_spirv(context, optimized_spirv.*.code, optimized_spirv.*.wordCount, &ir);
|
||||
|
||||
// var compiler: c.spvc_compiler = undefined;
|
||||
// _ = c.spvc_context_create_compiler(
|
||||
// context,
|
||||
// spvcBackend(out_lang),
|
||||
// ir,
|
||||
// c.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
|
||||
// &compiler,
|
||||
// );
|
||||
|
||||
// var resources: c.spvc_resources = undefined;
|
||||
// _ = c.spvc_compiler_create_shader_resources(compiler, &resources);
|
||||
|
||||
// var options: c.spvc_compiler_options = undefined;
|
||||
// _ = c.spvc_compiler_create_compiler_options(compiler, &options);
|
||||
// switch (out_lang) {
|
||||
// .glsl => {
|
||||
// const resource_types = [_]c.spvc_resource_type{
|
||||
// c.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
|
||||
// c.SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
|
||||
// c.SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
|
||||
// c.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
|
||||
// c.SPVC_RESOURCE_TYPE_SEPARATE_IMAGE,
|
||||
// c.SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS,
|
||||
// };
|
||||
// for (resource_types) |resource_type| {
|
||||
// glslRemapResources(compiler, resources, resource_type, bindings orelse &.{});
|
||||
// }
|
||||
|
||||
// _ = c.spvc_compiler_options_set_uint(options, c.SPVC_COMPILER_OPTION_GLSL_VERSION, 450);
|
||||
// _ = c.spvc_compiler_options_set_bool(options, c.SPVC_COMPILER_OPTION_GLSL_ES, c.SPVC_FALSE);
|
||||
// if (entrypoint) |e| {
|
||||
// _ = c.spvc_compiler_set_entry_point(compiler, e.name, spvExecutionModel(e.stage));
|
||||
// }
|
||||
|
||||
// // combiner samplers/textures
|
||||
// var id: c.spvc_variable_id = undefined;
|
||||
// res = c.spvc_compiler_build_dummy_sampler_for_combined_images(compiler, &id);
|
||||
// if (res == c.SPVC_SUCCESS) {
|
||||
// c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationDescriptorSet, 0);
|
||||
// c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationBinding, 0);
|
||||
// }
|
||||
// _ = c.spvc_compiler_build_combined_image_samplers(compiler);
|
||||
// },
|
||||
// else => @panic("TODO"),
|
||||
// }
|
||||
// _ = c.spvc_compiler_install_compiler_options(compiler, options);
|
||||
|
||||
// var source: [*c]const u8 = undefined;
|
||||
// _ = c.spvc_compiler_compile(compiler, &source);
|
||||
|
||||
// return allocator.dupe(u8, std.mem.span(source));
|
||||
// }
|
||||
|
||||
// Direct translation
|
||||
return switch (out_lang) {
|
||||
.spirv => try genSpirv(allocator, air, debug_info),
|
||||
.hlsl => try genHlsl(allocator, air, debug_info),
|
||||
.msl => try genMsl(allocator, air, debug_info, entrypoint, bindings, label orelse "<ShaderModule label not specified>"),
|
||||
.glsl => try genGlsl(allocator, air, debug_info, entrypoint, bindings),
|
||||
};
|
||||
}
|
||||
|
||||
fn spvMessageConsumer(
|
||||
level: c.spv_message_level_t,
|
||||
src: [*c]const u8,
|
||||
pos: [*c]const c.spv_position_t,
|
||||
msg: [*c]const u8,
|
||||
) callconv(.C) void {
|
||||
switch (level) {
|
||||
c.SPV_MSG_FATAL,
|
||||
c.SPV_MSG_INTERNAL_ERROR,
|
||||
c.SPV_MSG_ERROR,
|
||||
=> {
|
||||
// TODO - don't panic
|
||||
std.debug.panic("{s} at :{d}:{d}\n{s}", .{
|
||||
std.mem.span(msg),
|
||||
pos.*.line,
|
||||
pos.*.column,
|
||||
std.mem.span(src),
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn spvTargetEnv(language: Language) c.spv_target_env {
|
||||
return switch (language) {
|
||||
.glsl => c.SPV_ENV_OPENGL_4_5,
|
||||
.spirv => c.SPV_ENV_VULKAN_1_0,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn spvExecutionModel(stage: Stage) c.SpvExecutionModel {
|
||||
return switch (stage) {
|
||||
.vertex => c.SpvExecutionModelVertex,
|
||||
.fragment => c.SpvExecutionModelFragment,
|
||||
.compute => c.SpvExecutionModelGLCompute,
|
||||
};
|
||||
}
|
||||
|
||||
fn spvcErrorCallback(userdata: ?*anyopaque, err: [*c]const u8) callconv(.C) void {
|
||||
_ = userdata;
|
||||
// TODO - don't panic
|
||||
@panic(std.mem.span(err));
|
||||
}
|
||||
|
||||
fn spvcBackend(language: Language) c_uint {
|
||||
return switch (language) {
|
||||
.glsl => c.SPVC_BACKEND_GLSL,
|
||||
.hlsl => c.SPVC_BACKEND_HLSL,
|
||||
.msl => c.SPVC_BACKEND_MSL,
|
||||
.spirv => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn glslRemapResources(
|
||||
compiler: c.spvc_compiler,
|
||||
resources: c.spvc_resources,
|
||||
resource_type: c.spvc_resource_type,
|
||||
bindings: *const BindingTable,
|
||||
) void {
|
||||
var resource_list: [*c]c.spvc_reflected_resource = undefined;
|
||||
var resource_size: usize = undefined;
|
||||
_ = c.spvc_resources_get_resource_list_for_type(resources, resource_type, &resource_list, &resource_size);
|
||||
|
||||
for (resource_list[0..resource_size]) |resource| {
|
||||
const key = BindingPoint{
|
||||
.group = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet),
|
||||
.binding = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationBinding),
|
||||
};
|
||||
|
||||
if (bindings.get(key)) |slot| {
|
||||
_ = c.spvc_compiler_unset_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet);
|
||||
_ = c.spvc_compiler_set_decoration(compiler, resource.id, c.SpvDecorationBinding, slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/sysgpu/shader/ErrorList.zig
Normal file
128
src/sysgpu/shader/ErrorList.zig
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
const std = @import("std");
|
||||
const Token = @import("Token.zig");
|
||||
pub const ErrorList = @This();
|
||||
|
||||
pub const ErrorMsg = struct {
|
||||
loc: Token.Loc,
|
||||
msg: []const u8,
|
||||
note: ?Note = null,
|
||||
|
||||
pub const Note = struct {
|
||||
loc: ?Token.Loc = null,
|
||||
msg: []const u8,
|
||||
};
|
||||
};
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
list: std.ArrayListUnmanaged(ErrorMsg) = .{},
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !ErrorList {
|
||||
return .{
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ErrorList) void {
|
||||
self.arena.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
self: *ErrorList,
|
||||
loc: Token.Loc,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
note: ?ErrorMsg.Note,
|
||||
) !void {
|
||||
const err_msg = .{
|
||||
.loc = loc,
|
||||
.msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args),
|
||||
.note = note,
|
||||
};
|
||||
try self.list.append(self.arena.allocator(), err_msg);
|
||||
}
|
||||
|
||||
pub fn createNote(
|
||||
self: *ErrorList,
|
||||
loc: ?Token.Loc,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) !ErrorMsg.Note {
|
||||
return .{
|
||||
.loc = loc,
|
||||
.msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print(self: ErrorList, source: []const u8, file_path: ?[]const u8) !void {
|
||||
const stderr = std.io.getStdErr();
|
||||
var bw = std.io.bufferedWriter(stderr.writer());
|
||||
const b = bw.writer();
|
||||
const term = std.io.tty.detectConfig(stderr);
|
||||
|
||||
for (self.list.items) |*err| {
|
||||
const loc_extra = err.loc.extraInfo(source);
|
||||
|
||||
// 'file:line:column error: MSG'
|
||||
try term.setColor(b, .bold);
|
||||
try b.print("{?s}:{d}:{d} ", .{ file_path, loc_extra.line, loc_extra.col });
|
||||
try term.setColor(b, .bright_red);
|
||||
try b.writeAll("error: ");
|
||||
try term.setColor(b, .reset);
|
||||
try term.setColor(b, .bold);
|
||||
try b.writeAll(err.msg);
|
||||
try b.writeByte('\n');
|
||||
|
||||
try printCode(b, term, source, err.loc);
|
||||
|
||||
// note
|
||||
if (err.note) |note| {
|
||||
if (note.loc) |note_loc| {
|
||||
const note_loc_extra = note_loc.extraInfo(source);
|
||||
|
||||
try term.setColor(b, .reset);
|
||||
try term.setColor(b, .bold);
|
||||
try b.print("{?s}:{d}:{d} ", .{ file_path, note_loc_extra.line, note_loc_extra.col });
|
||||
}
|
||||
try term.setColor(b, .cyan);
|
||||
try b.writeAll("note: ");
|
||||
|
||||
try term.setColor(b, .reset);
|
||||
try term.setColor(b, .bold);
|
||||
try b.writeAll(note.msg);
|
||||
try b.writeByte('\n');
|
||||
|
||||
if (note.loc) |note_loc| {
|
||||
try printCode(b, term, source, note_loc);
|
||||
}
|
||||
}
|
||||
|
||||
try term.setColor(b, .reset);
|
||||
}
|
||||
try bw.flush();
|
||||
}
|
||||
|
||||
fn printCode(writer: anytype, term: std.io.tty.Config, source: []const u8, loc: Token.Loc) !void {
|
||||
const loc_extra = loc.extraInfo(source);
|
||||
try term.setColor(writer, .dim);
|
||||
try writer.print("{d} │ ", .{loc_extra.line});
|
||||
try term.setColor(writer, .reset);
|
||||
try writer.writeAll(source[loc_extra.line_start..loc.start]);
|
||||
try term.setColor(writer, .green);
|
||||
try writer.writeAll(source[loc.start..loc.end]);
|
||||
try term.setColor(writer, .reset);
|
||||
try writer.writeAll(source[loc.end..loc_extra.line_end]);
|
||||
try writer.writeByte('\n');
|
||||
|
||||
// location pointer
|
||||
const line_number_len = (std.math.log10(loc_extra.line) + 1) + 3;
|
||||
try writer.writeByteNTimes(
|
||||
' ',
|
||||
line_number_len + (loc_extra.col - 1),
|
||||
);
|
||||
try term.setColor(writer, .bold);
|
||||
try term.setColor(writer, .green);
|
||||
try writer.writeByte('^');
|
||||
try writer.writeByteNTimes('~', loc.end - loc.start - 1);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
2054
src/sysgpu/shader/Parser.zig
Normal file
2054
src/sysgpu/shader/Parser.zig
Normal file
File diff suppressed because it is too large
Load diff
518
src/sysgpu/shader/Token.zig
Normal file
518
src/sysgpu/shader/Token.zig
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
const std = @import("std");
|
||||
|
||||
tag: Tag,
|
||||
loc: Loc,
|
||||
|
||||
pub const Loc = struct {
|
||||
start: u32,
|
||||
end: u32,
|
||||
|
||||
const Extra = struct {
|
||||
line: u32,
|
||||
col: u32,
|
||||
line_start: u32,
|
||||
line_end: u32,
|
||||
};
|
||||
|
||||
pub fn slice(self: Loc, source: []const u8) []const u8 {
|
||||
return source[self.start..self.end];
|
||||
}
|
||||
|
||||
pub fn extraInfo(self: Loc, source: []const u8) Extra {
|
||||
var result = Extra{
|
||||
.line = 1,
|
||||
.col = 1,
|
||||
.line_start = 0,
|
||||
.line_end = @intCast(source.len),
|
||||
};
|
||||
|
||||
for (source[0..self.start], 0..) |c, i| {
|
||||
if (c == '\n') {
|
||||
result.line += 1;
|
||||
result.line_start = @as(u32, @intCast(i)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (source[self.end..], 0..) |c, i| {
|
||||
if (c == '\n') {
|
||||
result.line_end = self.end + @as(u32, @intCast(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.col += self.start - result.line_start;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Tag = enum {
|
||||
eof,
|
||||
invalid,
|
||||
|
||||
ident,
|
||||
number,
|
||||
|
||||
paren_left,
|
||||
paren_right,
|
||||
brace_left,
|
||||
brace_right,
|
||||
bracket_left,
|
||||
bracket_right,
|
||||
|
||||
dot,
|
||||
comma,
|
||||
colon,
|
||||
semicolon,
|
||||
arrow,
|
||||
attr,
|
||||
|
||||
equal,
|
||||
equal_equal,
|
||||
bang,
|
||||
bang_equal,
|
||||
ampersand,
|
||||
ampersand_equal,
|
||||
ampersand_ampersand,
|
||||
pipe,
|
||||
pipe_equal,
|
||||
pipe_pipe,
|
||||
tilde,
|
||||
plus,
|
||||
plus_equal,
|
||||
plus_plus,
|
||||
minus,
|
||||
minus_equal,
|
||||
minus_minus,
|
||||
asterisk,
|
||||
asterisk_equal,
|
||||
slash,
|
||||
slash_equal,
|
||||
percent,
|
||||
percent_equal,
|
||||
xor,
|
||||
xor_equal,
|
||||
angle_bracket_left,
|
||||
angle_bracket_left_equal,
|
||||
angle_bracket_angle_bracket_left,
|
||||
angle_bracket_angle_bracket_left_equal,
|
||||
angle_bracket_right,
|
||||
angle_bracket_right_equal,
|
||||
angle_bracket_angle_bracket_right,
|
||||
angle_bracket_angle_bracket_right_equal,
|
||||
underscore,
|
||||
|
||||
k_enable,
|
||||
k_requires,
|
||||
|
||||
k_fn,
|
||||
k_var,
|
||||
k_let,
|
||||
k_const,
|
||||
k_override,
|
||||
k_type,
|
||||
|
||||
k_if,
|
||||
k_else,
|
||||
k_loop,
|
||||
k_while,
|
||||
k_for,
|
||||
k_break,
|
||||
k_continue,
|
||||
k_continuing,
|
||||
k_discard,
|
||||
k_switch,
|
||||
k_case,
|
||||
k_default,
|
||||
k_return,
|
||||
k_const_assert,
|
||||
k_bitcast,
|
||||
|
||||
k_bool,
|
||||
k_u32,
|
||||
k_i32,
|
||||
k_f16,
|
||||
k_f32,
|
||||
k_vec2,
|
||||
k_vec3,
|
||||
k_vec4,
|
||||
k_mat2x2,
|
||||
k_mat2x3,
|
||||
k_mat2x4,
|
||||
k_mat3x2,
|
||||
k_mat3x3,
|
||||
k_mat3x4,
|
||||
k_mat4x2,
|
||||
k_mat4x3,
|
||||
k_mat4x4,
|
||||
k_ptr,
|
||||
k_array,
|
||||
k_atomic,
|
||||
k_struct,
|
||||
k_sampler,
|
||||
k_sampler_comparison,
|
||||
k_texture_depth_2d,
|
||||
k_texture_depth_2d_array,
|
||||
k_texture_depth_cube,
|
||||
k_texture_depth_cube_array,
|
||||
k_texture_depth_multisampled_2d,
|
||||
k_texture_external,
|
||||
k_texture_multisampled_2d,
|
||||
k_texture_1d,
|
||||
k_texture_2d,
|
||||
k_texture_2d_array,
|
||||
k_texture_3d,
|
||||
k_texture_cube,
|
||||
k_texture_cube_array,
|
||||
k_texture_storage_1d,
|
||||
k_texture_storage_2d,
|
||||
k_texture_storage_2d_array,
|
||||
k_texture_storage_3d,
|
||||
|
||||
k_false,
|
||||
k_true,
|
||||
|
||||
template_left,
|
||||
template_right,
|
||||
|
||||
pub fn symbol(self: Tag) []const u8 {
|
||||
return switch (self) {
|
||||
.eof => "EOF",
|
||||
.invalid => "invalid bytes",
|
||||
.ident => "an identifier",
|
||||
.number => "a number literal",
|
||||
|
||||
.paren_left => "(",
|
||||
.paren_right => ")",
|
||||
.brace_left => "{",
|
||||
.brace_right => "}",
|
||||
.bracket_left => "[",
|
||||
.bracket_right => "]",
|
||||
.dot => ".",
|
||||
.comma => ",",
|
||||
.colon => ":",
|
||||
.semicolon => ";",
|
||||
.arrow => "->",
|
||||
.attr => "@",
|
||||
.equal => "=",
|
||||
.equal_equal => "==",
|
||||
.bang => "!",
|
||||
.bang_equal => "!=",
|
||||
.ampersand => "&",
|
||||
.ampersand_equal => "&=",
|
||||
.ampersand_ampersand => "&&",
|
||||
.pipe => "|",
|
||||
.pipe_equal => "|=",
|
||||
.pipe_pipe => "||",
|
||||
.tilde => "~",
|
||||
.plus => "+",
|
||||
.plus_equal => "+=",
|
||||
.plus_plus => "++",
|
||||
.minus => "-",
|
||||
.minus_equal => "-=",
|
||||
.minus_minus => "--",
|
||||
.asterisk => "*",
|
||||
.asterisk_equal => "*=",
|
||||
.slash => "/",
|
||||
.slash_equal => "/=",
|
||||
.percent => "%",
|
||||
.percent_equal => "%=",
|
||||
.xor => "^",
|
||||
.xor_equal => "^=",
|
||||
.angle_bracket_left => "<",
|
||||
.angle_bracket_left_equal => "<=",
|
||||
.angle_bracket_angle_bracket_left => "<<",
|
||||
.angle_bracket_angle_bracket_left_equal => "<<=",
|
||||
.angle_bracket_right => ">",
|
||||
.angle_bracket_right_equal => ">=",
|
||||
.angle_bracket_angle_bracket_right => ">>",
|
||||
.angle_bracket_angle_bracket_right_equal => ">>=",
|
||||
.underscore => "_",
|
||||
.k_enable => "enable",
|
||||
.k_requires => "requires",
|
||||
.k_fn => "fn",
|
||||
.k_var => "var",
|
||||
.k_let => "let",
|
||||
.k_const => "const",
|
||||
.k_override => "override",
|
||||
.k_type => "type",
|
||||
.k_if => "if",
|
||||
.k_else => "else",
|
||||
.k_loop => "loop",
|
||||
.k_while => "while",
|
||||
.k_for => "for",
|
||||
.k_break => "break",
|
||||
.k_continue => "continue",
|
||||
.k_continuing => "continuing",
|
||||
.k_discard => "discard",
|
||||
.k_switch => "switch",
|
||||
.k_case => "case",
|
||||
.k_default => "default",
|
||||
.k_return => "return",
|
||||
.k_const_assert => "const_assert",
|
||||
.k_bitcast => "bitcast",
|
||||
.k_bool => "bool",
|
||||
.k_u32 => "u32",
|
||||
.k_i32 => "i32",
|
||||
.k_f16 => "f16",
|
||||
.k_f32 => "f32",
|
||||
.k_vec2 => "vec2",
|
||||
.k_vec3 => "vec3",
|
||||
.k_vec4 => "vec4",
|
||||
.k_mat2x2 => "mat2x2",
|
||||
.k_mat2x3 => "mat2x3",
|
||||
.k_mat2x4 => "mat2x4",
|
||||
.k_mat3x2 => "mat3x2",
|
||||
.k_mat3x3 => "mat3x3",
|
||||
.k_mat3x4 => "mat3x4",
|
||||
.k_mat4x2 => "mat4x2",
|
||||
.k_mat4x3 => "mat4x3",
|
||||
.k_mat4x4 => "mat4x4",
|
||||
.k_ptr => "ptr",
|
||||
.k_array => "array",
|
||||
.k_atomic => "atomic",
|
||||
.k_struct => "struct",
|
||||
.k_sampler => "sampler",
|
||||
.k_sampler_comparison => "sampler_comparison",
|
||||
.k_texture_depth_2d => "texture_depth_2d",
|
||||
.k_texture_depth_2d_array => "texture_depth_2d_array",
|
||||
.k_texture_depth_cube => "texture_depth_cube",
|
||||
.k_texture_depth_cube_array => "texture_depth_cube_array",
|
||||
.k_texture_depth_multisampled_2d => "texture_depth_multisampled_2d",
|
||||
.k_texture_external => "texture_external",
|
||||
.k_texture_multisampled_2d => "texture_multisampled_2d",
|
||||
.k_texture_1d => "texture_1d",
|
||||
.k_texture_2d => "texture_2d",
|
||||
.k_texture_2d_array => "texture_2d_array",
|
||||
.k_texture_3d => "texture_3d",
|
||||
.k_texture_cube => "texture_cube",
|
||||
.k_texture_cube_array => "texture_cube_array",
|
||||
.k_texture_storage_1d => "texture_storage_1d",
|
||||
.k_texture_storage_2d => "texture_storage_2d",
|
||||
.k_texture_storage_2d_array => "texture_storage_2d_array",
|
||||
.k_texture_storage_3d => "texture_storage_3d",
|
||||
.k_false => "false",
|
||||
.k_true => "true",
|
||||
.template_left => "<",
|
||||
.template_right => ">",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const keywords = std.ComptimeStringMap(Tag, .{
|
||||
.{ "enable", .k_enable },
|
||||
.{ "requires", .k_requires },
|
||||
.{ "fn", .k_fn },
|
||||
.{ "var", .k_var },
|
||||
.{ "let", .k_let },
|
||||
.{ "const", .k_const },
|
||||
.{ "override", .k_override },
|
||||
.{ "type", .k_type },
|
||||
.{ "if", .k_if },
|
||||
.{ "else", .k_else },
|
||||
.{ "loop", .k_loop },
|
||||
.{ "while", .k_while },
|
||||
.{ "for", .k_for },
|
||||
.{ "break", .k_break },
|
||||
.{ "continue", .k_continue },
|
||||
.{ "continuing", .k_continuing },
|
||||
.{ "discard", .k_discard },
|
||||
.{ "switch", .k_switch },
|
||||
.{ "case", .k_case },
|
||||
.{ "default", .k_default },
|
||||
.{ "return", .k_return },
|
||||
.{ "const_assert", .k_const_assert },
|
||||
.{ "bitcast", .k_bitcast },
|
||||
.{ "bool", .k_bool },
|
||||
.{ "u32", .k_u32 },
|
||||
.{ "i32", .k_i32 },
|
||||
.{ "f16", .k_f16 },
|
||||
.{ "f32", .k_f32 },
|
||||
.{ "vec2", .k_vec2 },
|
||||
.{ "vec3", .k_vec3 },
|
||||
.{ "vec4", .k_vec4 },
|
||||
.{ "mat2x2", .k_mat2x2 },
|
||||
.{ "mat2x3", .k_mat2x3 },
|
||||
.{ "mat2x4", .k_mat2x4 },
|
||||
.{ "mat3x2", .k_mat3x2 },
|
||||
.{ "mat3x3", .k_mat3x3 },
|
||||
.{ "mat3x4", .k_mat3x4 },
|
||||
.{ "mat4x2", .k_mat4x2 },
|
||||
.{ "mat4x3", .k_mat4x3 },
|
||||
.{ "mat4x4", .k_mat4x4 },
|
||||
.{ "ptr", .k_ptr },
|
||||
.{ "array", .k_array },
|
||||
.{ "atomic", .k_atomic },
|
||||
.{ "struct", .k_struct },
|
||||
.{ "sampler", .k_sampler },
|
||||
.{ "sampler_comparison", .k_sampler_comparison },
|
||||
.{ "texture_depth_2d", .k_texture_depth_2d },
|
||||
.{ "texture_depth_2d_array", .k_texture_depth_2d_array },
|
||||
.{ "texture_depth_cube", .k_texture_depth_cube },
|
||||
.{ "texture_depth_cube_array", .k_texture_depth_cube_array },
|
||||
.{ "texture_depth_multisampled_2d", .k_texture_depth_multisampled_2d },
|
||||
.{ "texture_external", .k_texture_external },
|
||||
.{ "texture_multisampled_2d", .k_texture_multisampled_2d },
|
||||
.{ "texture_1d", .k_texture_1d },
|
||||
.{ "texture_2d", .k_texture_2d },
|
||||
.{ "texture_2d_array", .k_texture_2d_array },
|
||||
.{ "texture_3d", .k_texture_3d },
|
||||
.{ "texture_cube", .k_texture_cube },
|
||||
.{ "texture_cube_array", .k_texture_cube_array },
|
||||
.{ "texture_storage_1d", .k_texture_storage_1d },
|
||||
.{ "texture_storage_2d", .k_texture_storage_2d },
|
||||
.{ "texture_storage_2d_array", .k_texture_storage_2d_array },
|
||||
.{ "texture_storage_3d", .k_texture_storage_3d },
|
||||
.{ "false", .k_false },
|
||||
.{ "true", .k_true },
|
||||
});
|
||||
|
||||
pub const reserved = blk: {
|
||||
@setEvalBranchQuota(3000);
|
||||
break :blk std.ComptimeStringMap(void, .{
|
||||
.{ "NULL", {} },
|
||||
.{ "Self", {} },
|
||||
.{ "abstract", {} },
|
||||
.{ "active", {} },
|
||||
.{ "alignas", {} },
|
||||
.{ "alignof", {} },
|
||||
.{ "as", {} },
|
||||
.{ "asm", {} },
|
||||
.{ "asm_fragment", {} },
|
||||
.{ "async", {} },
|
||||
.{ "attribute", {} },
|
||||
.{ "auto", {} },
|
||||
.{ "await", {} },
|
||||
.{ "become", {} },
|
||||
.{ "binding_array", {} },
|
||||
.{ "cast", {} },
|
||||
.{ "catch", {} },
|
||||
.{ "class", {} },
|
||||
.{ "co_await", {} },
|
||||
.{ "co_return", {} },
|
||||
.{ "co_yield", {} },
|
||||
.{ "coherent", {} },
|
||||
.{ "column_major", {} },
|
||||
.{ "common", {} },
|
||||
.{ "compile", {} },
|
||||
.{ "compile_fragment", {} },
|
||||
.{ "concept", {} },
|
||||
.{ "const_cast", {} },
|
||||
.{ "consteval", {} },
|
||||
.{ "constexpr", {} },
|
||||
.{ "constinit", {} },
|
||||
.{ "crate", {} },
|
||||
.{ "debugger", {} },
|
||||
.{ "decltype", {} },
|
||||
.{ "delete", {} },
|
||||
.{ "demote", {} },
|
||||
.{ "demote_to_helper", {} },
|
||||
.{ "do", {} },
|
||||
.{ "dynamic_cast", {} },
|
||||
.{ "enum", {} },
|
||||
.{ "explicit", {} },
|
||||
.{ "export", {} },
|
||||
.{ "extends", {} },
|
||||
.{ "extern", {} },
|
||||
.{ "external", {} },
|
||||
.{ "fallthrough", {} },
|
||||
.{ "filter", {} },
|
||||
.{ "final", {} },
|
||||
.{ "finally", {} },
|
||||
.{ "friend", {} },
|
||||
.{ "from", {} },
|
||||
.{ "fxgroup", {} },
|
||||
.{ "get", {} },
|
||||
.{ "goto", {} },
|
||||
.{ "groupshared", {} },
|
||||
.{ "highp", {} },
|
||||
.{ "impl", {} },
|
||||
.{ "implements", {} },
|
||||
.{ "import", {} },
|
||||
.{ "inline", {} },
|
||||
.{ "instanceof", {} },
|
||||
.{ "interface", {} },
|
||||
.{ "layout", {} },
|
||||
.{ "lowp", {} },
|
||||
.{ "macro", {} },
|
||||
.{ "macro_rules", {} },
|
||||
.{ "match", {} },
|
||||
.{ "mediump", {} },
|
||||
.{ "meta", {} },
|
||||
.{ "mod", {} },
|
||||
.{ "module", {} },
|
||||
.{ "move", {} },
|
||||
.{ "mut", {} },
|
||||
.{ "mutable", {} },
|
||||
.{ "namespace", {} },
|
||||
.{ "new", {} },
|
||||
.{ "nil", {} },
|
||||
.{ "noexcept", {} },
|
||||
.{ "noinline", {} },
|
||||
.{ "nointerpolation", {} },
|
||||
.{ "noperspective", {} },
|
||||
.{ "null", {} },
|
||||
.{ "nullptr", {} },
|
||||
.{ "of", {} },
|
||||
.{ "operator", {} },
|
||||
.{ "package", {} },
|
||||
.{ "packoffset", {} },
|
||||
.{ "partition", {} },
|
||||
.{ "pass", {} },
|
||||
.{ "patch", {} },
|
||||
.{ "pixelfragment", {} },
|
||||
.{ "precise", {} },
|
||||
.{ "precision", {} },
|
||||
.{ "premerge", {} },
|
||||
.{ "priv", {} },
|
||||
.{ "protected", {} },
|
||||
.{ "pub", {} },
|
||||
.{ "public", {} },
|
||||
.{ "readonly", {} },
|
||||
.{ "ref", {} },
|
||||
.{ "regardless", {} },
|
||||
.{ "register", {} },
|
||||
.{ "reinterpret_cast", {} },
|
||||
.{ "require", {} },
|
||||
.{ "resource", {} },
|
||||
.{ "restrict", {} },
|
||||
.{ "self", {} },
|
||||
.{ "set", {} },
|
||||
.{ "shared", {} },
|
||||
.{ "sizeof", {} },
|
||||
.{ "smooth", {} },
|
||||
.{ "snorm", {} },
|
||||
.{ "static", {} },
|
||||
.{ "static_assert", {} },
|
||||
.{ "static_cast", {} },
|
||||
.{ "std", {} },
|
||||
.{ "subroutine", {} },
|
||||
.{ "super", {} },
|
||||
.{ "target", {} },
|
||||
.{ "template", {} },
|
||||
.{ "this", {} },
|
||||
.{ "thread_local", {} },
|
||||
.{ "throw", {} },
|
||||
.{ "trait", {} },
|
||||
.{ "try", {} },
|
||||
.{ "type", {} },
|
||||
.{ "typedef", {} },
|
||||
.{ "typeid", {} },
|
||||
.{ "typename", {} },
|
||||
.{ "typeof", {} },
|
||||
.{ "union", {} },
|
||||
.{ "unless", {} },
|
||||
.{ "unorm", {} },
|
||||
.{ "unsafe", {} },
|
||||
.{ "unsized", {} },
|
||||
.{ "use", {} },
|
||||
.{ "using", {} },
|
||||
.{ "varying", {} },
|
||||
.{ "virtual", {} },
|
||||
.{ "volatile", {} },
|
||||
.{ "wgsl", {} },
|
||||
.{ "where", {} },
|
||||
.{ "with", {} },
|
||||
.{ "writeonly", {} },
|
||||
.{ "yield", {} },
|
||||
});
|
||||
};
|
||||
437
src/sysgpu/shader/Tokenizer.zig
Normal file
437
src/sysgpu/shader/Tokenizer.zig
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
const std = @import("std");
|
||||
const Token = @import("Token.zig");
|
||||
|
||||
const Tokenizer = @This();
|
||||
|
||||
source: [:0]const u8,
|
||||
index: u32 = 0,
|
||||
|
||||
const State = union(enum) {
|
||||
start,
|
||||
ident,
|
||||
underscore,
|
||||
number: struct {
|
||||
is_hex: bool = false,
|
||||
allow_leading_sign: bool = false,
|
||||
has_dot: bool = false,
|
||||
},
|
||||
block_comment,
|
||||
ampersand,
|
||||
bang,
|
||||
equal,
|
||||
angle_bracket_left,
|
||||
angle_bracket_angle_bracket_left,
|
||||
angle_bracket_right,
|
||||
angle_bracket_angle_bracket_right,
|
||||
minus,
|
||||
percent,
|
||||
dot,
|
||||
pipe,
|
||||
plus,
|
||||
slash,
|
||||
asterisk,
|
||||
xor,
|
||||
};
|
||||
|
||||
pub fn init(source: [:0]const u8) Tokenizer {
|
||||
// skip the UTF-8 BOM if present
|
||||
const src_start: u32 = if (std.mem.startsWith(u8, source, "\xEF\xBB\xBF")) 3 else 0;
|
||||
return Tokenizer{ .source = source[src_start..] };
|
||||
}
|
||||
|
||||
pub fn peek(tokenizer: *Tokenizer) Token {
|
||||
var index = tokenizer.index;
|
||||
var state: State = .start;
|
||||
var result = Token{
|
||||
.tag = .eof,
|
||||
.loc = .{
|
||||
.start = index,
|
||||
.end = undefined,
|
||||
},
|
||||
};
|
||||
|
||||
while (true) : (index += 1) {
|
||||
const c = tokenizer.source[index];
|
||||
switch (state) {
|
||||
.start => switch (c) {
|
||||
0 => {
|
||||
if (index != tokenizer.source.len) {
|
||||
result.tag = .invalid;
|
||||
index += 1;
|
||||
}
|
||||
break;
|
||||
},
|
||||
' ', '\n', '\t', '\r' => result.loc.start = index + 1,
|
||||
|
||||
'a'...'z', 'A'...'Z' => state = .ident,
|
||||
'0'...'9' => state = .{ .number = .{} },
|
||||
'&' => state = .ampersand,
|
||||
'!' => state = .bang,
|
||||
'=' => state = .equal,
|
||||
'<' => state = .angle_bracket_left,
|
||||
'>' => state = .angle_bracket_right,
|
||||
'-' => state = .minus,
|
||||
'%' => state = .percent,
|
||||
'.' => state = .dot,
|
||||
'|' => state = .pipe,
|
||||
'+' => state = .plus,
|
||||
'/' => state = .slash,
|
||||
'*' => state = .asterisk,
|
||||
'_' => state = .underscore,
|
||||
'^' => state = .xor,
|
||||
|
||||
'@' => {
|
||||
result.tag = .attr;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'[' => {
|
||||
result.tag = .bracket_left;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
']' => {
|
||||
result.tag = .bracket_right;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'{' => {
|
||||
result.tag = .brace_left;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'}' => {
|
||||
result.tag = .brace_right;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
':' => {
|
||||
result.tag = .colon;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
',' => {
|
||||
result.tag = .comma;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'(' => {
|
||||
result.tag = .paren_left;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
')' => {
|
||||
result.tag = .paren_right;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
';' => {
|
||||
result.tag = .semicolon;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'~' => {
|
||||
result.tag = .tilde;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
|
||||
else => {
|
||||
result.tag = .invalid;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.ident => switch (c) {
|
||||
'a'...'z', 'A'...'Z', '0'...'9', '_' => {},
|
||||
else => {
|
||||
result.tag = .ident;
|
||||
if (Token.keywords.get(tokenizer.source[result.loc.start..index])) |tag| {
|
||||
result.tag = tag;
|
||||
} else if (Token.reserved.get(tokenizer.source[result.loc.start..index])) |_| {
|
||||
result.tag = .invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
.underscore => switch (c) { // TODO: two underscore `__` https://www.w3.org/TR/WGSL/#identifiers
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => state = .ident,
|
||||
else => {
|
||||
result.tag = .underscore;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.number => |*number| {
|
||||
result.tag = .number;
|
||||
switch (c) {
|
||||
'0'...'9' => {},
|
||||
'a'...'d', 'A'...'D' => if (!number.is_hex) break,
|
||||
'x', 'X' => number.is_hex = true,
|
||||
'.' => {
|
||||
if (number.has_dot) break;
|
||||
number.has_dot = true;
|
||||
},
|
||||
'+', '-' => {
|
||||
if (!number.allow_leading_sign) break;
|
||||
number.allow_leading_sign = false;
|
||||
number.is_hex = false;
|
||||
},
|
||||
'e', 'E' => if (!number.is_hex) {
|
||||
number.allow_leading_sign = true;
|
||||
},
|
||||
'p', 'P' => if (number.is_hex) {
|
||||
number.allow_leading_sign = true;
|
||||
},
|
||||
'i', 'u' => {
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'f', 'h' => if (!number.is_hex) {
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
},
|
||||
.block_comment => switch (c) {
|
||||
0 => break,
|
||||
'\n' => {
|
||||
state = .start;
|
||||
result.loc.start = index + 1;
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.ampersand => switch (c) {
|
||||
'&' => {
|
||||
result.tag = .ampersand_ampersand;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'=' => {
|
||||
result.tag = .ampersand_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .ampersand;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.bang => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .bang_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .bang;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.equal => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .equal_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .equal;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.angle_bracket_left => switch (c) {
|
||||
'<' => state = .angle_bracket_angle_bracket_left,
|
||||
'=' => {
|
||||
result.tag = .angle_bracket_left_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .angle_bracket_left;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.angle_bracket_angle_bracket_left => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .angle_bracket_angle_bracket_left_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .angle_bracket_angle_bracket_left;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.angle_bracket_right => switch (c) {
|
||||
'>' => state = .angle_bracket_angle_bracket_right,
|
||||
'=' => {
|
||||
result.tag = .angle_bracket_right_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .angle_bracket_right;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.angle_bracket_angle_bracket_right => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .angle_bracket_angle_bracket_right_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .angle_bracket_angle_bracket_right;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.minus => switch (c) {
|
||||
'-' => {
|
||||
result.tag = .minus_minus;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'=' => {
|
||||
result.tag = .minus_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'>' => {
|
||||
result.tag = .arrow;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'0'...'9' => {
|
||||
// workaround for x-1 being tokenized as [x] [-1]
|
||||
// TODO: maybe it's user fault? :^)
|
||||
// duplicated at .plus too
|
||||
if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) {
|
||||
result.tag = .minus;
|
||||
break;
|
||||
}
|
||||
state = .{ .number = .{} };
|
||||
},
|
||||
else => {
|
||||
result.tag = .minus;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.percent => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .percent_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .percent;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.pipe => switch (c) {
|
||||
'|' => {
|
||||
result.tag = .pipe_pipe;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'=' => {
|
||||
result.tag = .pipe_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .pipe;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.dot => switch (c) {
|
||||
'0'...'9' => state = .{ .number = .{} },
|
||||
else => {
|
||||
result.tag = .dot;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.plus => switch (c) {
|
||||
'+' => {
|
||||
result.tag = .plus_plus;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'=' => {
|
||||
result.tag = .plus_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
'0'...'9' => {
|
||||
if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) {
|
||||
result.tag = .plus;
|
||||
break;
|
||||
}
|
||||
state = .{ .number = .{} };
|
||||
},
|
||||
else => {
|
||||
result.tag = .plus;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.slash => switch (c) {
|
||||
'/' => state = .block_comment,
|
||||
'=' => {
|
||||
result.tag = .slash_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .slash;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.asterisk => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .asterisk_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .asterisk;
|
||||
break;
|
||||
},
|
||||
},
|
||||
.xor => switch (c) {
|
||||
'=' => {
|
||||
result.tag = .xor_equal;
|
||||
index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.tag = .xor;
|
||||
break;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
result.loc.end = index;
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn next(tokenizer: *Tokenizer) Token {
|
||||
const tok = tokenizer.peek();
|
||||
tokenizer.index = tok.loc.end;
|
||||
return tok;
|
||||
}
|
||||
|
||||
// test "tokenize identifier and numbers" {
|
||||
// const str =
|
||||
// \\_ __ _iden iden -100i 100.8i // cc
|
||||
// \\// comment
|
||||
// \\
|
||||
// ;
|
||||
// var tokenizer = Tokenizer.init(str);
|
||||
// try std.testing.expect(tokenizer.next().tag == .underscore);
|
||||
// try std.testing.expect(tokenizer.next().tag == .ident);
|
||||
// try std.testing.expect(tokenizer.next().tag == .ident);
|
||||
// try std.testing.expect(tokenizer.next().tag == .ident);
|
||||
// try std.testing.expectEqualStrings("-100i", tokenizer.next().loc.slice(str));
|
||||
// try std.testing.expect(tokenizer.next().tag == .number);
|
||||
// try std.testing.expect(tokenizer.next().tag == .eof);
|
||||
// }
|
||||
903
src/sysgpu/shader/codegen/glsl.zig
Normal file
903
src/sysgpu/shader/codegen/glsl.zig
Normal file
|
|
@ -0,0 +1,903 @@
|
|||
const std = @import("std");
|
||||
const Air = @import("../Air.zig");
|
||||
const DebugInfo = @import("../CodeGen.zig").DebugInfo;
|
||||
const Entrypoint = @import("../CodeGen.zig").Entrypoint;
|
||||
const BindingPoint = @import("../CodeGen.zig").BindingPoint;
|
||||
const BindingTable = @import("../CodeGen.zig").BindingTable;
|
||||
const Inst = Air.Inst;
|
||||
const InstIndex = Air.InstIndex;
|
||||
const Builtin = Air.Inst.Builtin;
|
||||
|
||||
const Glsl = @This();
|
||||
|
||||
air: *const Air,
|
||||
allocator: std.mem.Allocator,
|
||||
storage: std.ArrayListUnmanaged(u8),
|
||||
writer: std.ArrayListUnmanaged(u8).Writer,
|
||||
bindings: *const BindingTable,
|
||||
entrypoint_inst: ?Inst.Fn = null,
|
||||
indent: u32 = 0,
|
||||
|
||||
pub fn gen(
|
||||
allocator: std.mem.Allocator,
|
||||
air: *const Air,
|
||||
debug_info: DebugInfo,
|
||||
entrypoint: ?Entrypoint,
|
||||
bindings: ?*const BindingTable,
|
||||
) ![]const u8 {
|
||||
_ = debug_info;
|
||||
|
||||
var storage = std.ArrayListUnmanaged(u8){};
|
||||
var glsl = Glsl{
|
||||
.air = air,
|
||||
.allocator = allocator,
|
||||
.storage = storage,
|
||||
.writer = storage.writer(allocator),
|
||||
.bindings = bindings orelse &.{},
|
||||
};
|
||||
defer {
|
||||
storage.deinit(allocator);
|
||||
}
|
||||
|
||||
try glsl.writeAll("#version 450\n\n");
|
||||
|
||||
for (air.refToList(air.globals_index)) |inst_idx| {
|
||||
switch (air.getInst(inst_idx)) {
|
||||
.@"struct" => |inst| try glsl.emitStruct(inst),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// GLSL deosn't support multiple entrypoints so we only generate
|
||||
// when `entrypoint` is specified OR there's only one entrypoint
|
||||
var entrypoint_name: ?[]const u8 = null;
|
||||
|
||||
if (entrypoint != null) {
|
||||
entrypoint_name = std.mem.span(entrypoint.?.name);
|
||||
} else {
|
||||
const has_multiple_entrypoints = @intFromBool(air.vertex_stage == .none) &
|
||||
@intFromBool(air.fragment_stage == .none) &
|
||||
@intFromBool(air.compute_stage == .none);
|
||||
|
||||
if (has_multiple_entrypoints == 1) {
|
||||
return error.MultipleEntrypoints;
|
||||
}
|
||||
}
|
||||
|
||||
for (air.refToList(air.globals_index)) |inst_idx| {
|
||||
switch (air.getInst(inst_idx)) {
|
||||
.@"var" => |inst| try glsl.emitGlobalVar(inst),
|
||||
.@"fn" => |inst| {
|
||||
const name = glsl.air.getStr(inst.name);
|
||||
if (entrypoint_name) |_| {
|
||||
if (std.mem.eql(u8, entrypoint_name.?, name)) {
|
||||
try glsl.emitFn(inst);
|
||||
}
|
||||
} else if (inst.stage != .none) {
|
||||
try glsl.emitFn(inst);
|
||||
}
|
||||
},
|
||||
.@"struct" => {},
|
||||
else => |inst| try glsl.print("TopLevel: {}\n", .{inst}), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
return storage.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn emitElemType(glsl: *Glsl, inst_idx: InstIndex) !void {
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.bool => |inst| try glsl.emitBoolElemType(inst),
|
||||
.int => |inst| try glsl.emitIntElemType(inst),
|
||||
.float => |inst| try glsl.emitFloatElemType(inst),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn emitBoolElemType(glsl: *Glsl, inst: Inst.Bool) !void {
|
||||
_ = inst;
|
||||
try glsl.writeAll("b");
|
||||
}
|
||||
|
||||
fn emitIntElemType(glsl: *Glsl, inst: Inst.Int) !void {
|
||||
try glsl.writeAll(switch (inst.type) {
|
||||
.u32 => "u",
|
||||
.i32 => "i",
|
||||
});
|
||||
}
|
||||
|
||||
fn emitFloatElemType(glsl: *Glsl, inst: Inst.Float) !void {
|
||||
try glsl.writeAll(switch (inst.type) {
|
||||
.f32 => "",
|
||||
.f16 => "", // TODO - extension for half support?
|
||||
});
|
||||
}
|
||||
|
||||
fn emitType(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
|
||||
if (inst_idx == .none) {
|
||||
try glsl.writeAll("void");
|
||||
} else {
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.bool => |inst| try glsl.emitBoolType(inst),
|
||||
.int => |inst| try glsl.emitIntType(inst),
|
||||
.float => |inst| try glsl.emitFloatType(inst),
|
||||
.vector => |inst| try glsl.emitVectorType(inst),
|
||||
.matrix => |inst| try glsl.emitMatrixType(inst),
|
||||
.array => |inst| try glsl.emitType(inst.elem_type),
|
||||
.@"struct" => |inst| try glsl.writeName(inst.name),
|
||||
else => |inst| try glsl.print("Type: {}", .{inst}), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emitTypeSuffix(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
|
||||
if (inst_idx != .none) {
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.array => |inst| try glsl.emitArrayTypeSuffix(inst),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emitArrayTypeSuffix(glsl: *Glsl, inst: Inst.Array) !void {
|
||||
if (inst.len != .none) {
|
||||
if (glsl.air.resolveInt(inst.len)) |len| {
|
||||
try glsl.print("[{}]", .{len});
|
||||
}
|
||||
} else {
|
||||
try glsl.writeAll("[]");
|
||||
}
|
||||
try glsl.emitTypeSuffix(inst.elem_type);
|
||||
}
|
||||
|
||||
fn emitBoolType(glsl: *Glsl, inst: Inst.Bool) !void {
|
||||
_ = inst;
|
||||
try glsl.writeAll("bool");
|
||||
}
|
||||
|
||||
fn emitIntType(glsl: *Glsl, inst: Inst.Int) !void {
|
||||
try glsl.writeAll(switch (inst.type) {
|
||||
.u32 => "uint",
|
||||
.i32 => "int",
|
||||
});
|
||||
}
|
||||
|
||||
fn emitFloatType(glsl: *Glsl, inst: Inst.Float) !void {
|
||||
try glsl.writeAll(switch (inst.type) {
|
||||
.f32 => "float",
|
||||
.f16 => "half",
|
||||
});
|
||||
}
|
||||
|
||||
fn emitVectorSize(glsl: *Glsl, size: Inst.Vector.Size) !void {
|
||||
try glsl.writeAll(switch (size) {
|
||||
.two => "2",
|
||||
.three => "3",
|
||||
.four => "4",
|
||||
});
|
||||
}
|
||||
|
||||
fn emitVectorType(glsl: *Glsl, inst: Inst.Vector) !void {
|
||||
try glsl.emitElemType(inst.elem_type);
|
||||
try glsl.writeAll("vec");
|
||||
try glsl.emitVectorSize(inst.size);
|
||||
}
|
||||
|
||||
fn emitMatrixType(glsl: *Glsl, inst: Inst.Matrix) !void {
|
||||
// TODO - verify dimension order
|
||||
try glsl.emitElemType(inst.elem_type);
|
||||
try glsl.writeAll("mat");
|
||||
try glsl.emitVectorSize(inst.cols);
|
||||
try glsl.writeAll("x");
|
||||
try glsl.emitVectorSize(inst.rows);
|
||||
}
|
||||
|
||||
fn emitStruct(glsl: *Glsl, inst: Inst.Struct) !void {
|
||||
// Workaround - structures with runtime arrays are not generally supported but can exist directly
|
||||
// in a block context which we inline in emitGlobalVar
|
||||
for (glsl.air.refToList(inst.members)) |member_index| {
|
||||
const member = glsl.air.getInst(member_index).struct_member;
|
||||
|
||||
switch (glsl.air.getInst(member.type)) {
|
||||
.array => |array_type| {
|
||||
if (array_type.len == .none) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
try glsl.writeAll("struct ");
|
||||
try glsl.writeName(inst.name);
|
||||
try glsl.writeAll(" {\n");
|
||||
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
for (glsl.air.refToList(inst.members)) |member_index| {
|
||||
const member = glsl.air.getInst(member_index).struct_member;
|
||||
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitType(member.type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(member.name);
|
||||
try glsl.emitTypeSuffix(member.type);
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
|
||||
try glsl.writeAll("};\n");
|
||||
}
|
||||
|
||||
fn emitBuiltin(glsl: *Glsl, builtin: Builtin) !void {
|
||||
const stage = glsl.entrypoint_inst.?.stage;
|
||||
try glsl.writeAll(switch (builtin) {
|
||||
.vertex_index => "gl_VertexID",
|
||||
.instance_index => "gl_InstanceID",
|
||||
.position => if (stage == .vertex) "gl_Position" else "gl_FragCoord",
|
||||
.front_facing => "gl_FrontFacing",
|
||||
.frag_depth => "gl_FragDepth",
|
||||
.local_invocation_id => "gl_LocalInvocationID",
|
||||
.local_invocation_index => "gl_LocalInvocationIndex",
|
||||
.global_invocation_id => "gl_GlobalInvocationID",
|
||||
.workgroup_id => "gl_WorkGroupID",
|
||||
.num_workgroups => "gl_NumWorkGroups",
|
||||
.sample_index => "gl_SampleID",
|
||||
.sample_mask => "gl_SampleMask", // TODO - gl_SampleMaskIn
|
||||
});
|
||||
}
|
||||
|
||||
fn emitGlobalVar(glsl: *Glsl, inst: Inst.Var) !void {
|
||||
const group = glsl.air.resolveInt(inst.group) orelse return error.ConstExpr;
|
||||
const binding = glsl.air.resolveInt(inst.binding) orelse return error.ConstExpr;
|
||||
const key = BindingPoint{ .group = @intCast(group), .binding = @intCast(binding) };
|
||||
const slot = glsl.bindings.get(key) orelse return error.NoBinding;
|
||||
|
||||
try glsl.print("layout(binding = {}, ", .{slot});
|
||||
try glsl.writeAll(if (inst.addr_space == .uniform) "std140" else "std430");
|
||||
try glsl.writeAll(") ");
|
||||
if (inst.access_mode == .read)
|
||||
try glsl.writeAll("readonly ");
|
||||
try glsl.writeAll(if (inst.addr_space == .uniform) "uniform" else "buffer");
|
||||
try glsl.print(" Block{}", .{slot});
|
||||
const var_type = glsl.air.getInst(inst.type);
|
||||
switch (var_type) {
|
||||
.@"struct" => |struct_inst| {
|
||||
// Inline struct to support runtime arrays
|
||||
try glsl.writeAll("\n");
|
||||
try glsl.writeAll("{\n");
|
||||
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
for (glsl.air.refToList(struct_inst.members)) |member_index| {
|
||||
const member = glsl.air.getInst(member_index).struct_member;
|
||||
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitType(member.type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(member.name);
|
||||
try glsl.emitTypeSuffix(member.type);
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
try glsl.writeAll("} ");
|
||||
try glsl.writeName(inst.name);
|
||||
try glsl.writeAll(";\n");
|
||||
},
|
||||
else => {
|
||||
try glsl.writeAll(" { ");
|
||||
try glsl.emitType(inst.type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(inst.name);
|
||||
try glsl.emitTypeSuffix(inst.type);
|
||||
try glsl.writeAll("; };\n");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn emitGlobal(glsl: *Glsl, location: ?u16, in_out: []const u8, var_type: InstIndex, name: Air.StringIndex) !void {
|
||||
try glsl.print("layout(location = {}) {s} ", .{ location.?, in_out });
|
||||
try glsl.emitType(var_type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(name);
|
||||
try glsl.emitTypeSuffix(var_type);
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
|
||||
fn emitGlobalFnParam(glsl: *Glsl, inst_idx: InstIndex) !void {
|
||||
const inst = glsl.air.getInst(inst_idx).fn_param;
|
||||
|
||||
if (inst.builtin == null) {
|
||||
try glsl.emitGlobal(inst.location, "in", inst.type, inst.name);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitGlobalStructOutputs(glsl: *Glsl, inst: Inst.Struct) !void {
|
||||
for (glsl.air.refToList(inst.members)) |member_index| {
|
||||
const member = glsl.air.getInst(member_index).struct_member;
|
||||
|
||||
if (member.builtin == null) {
|
||||
try glsl.emitGlobal(member.location, "out", member.type, member.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emitGlobalScalarOutput(glsl: *Glsl, inst: Inst.Fn) !void {
|
||||
if (inst.return_attrs.builtin == null) {
|
||||
try glsl.print("layout(location = {}) out ", .{0});
|
||||
try glsl.emitType(inst.return_type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeAll("main_output");
|
||||
try glsl.emitTypeSuffix(inst.return_type);
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn emitFn(glsl: *Glsl, inst: Inst.Fn) !void {
|
||||
if (inst.stage != .none) {
|
||||
glsl.entrypoint_inst = inst;
|
||||
|
||||
if (inst.params != .none) {
|
||||
const param_list = glsl.air.refToList(inst.params);
|
||||
for (param_list) |param_inst_idx| {
|
||||
try glsl.emitGlobalFnParam(param_inst_idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst.return_type != .none) {
|
||||
switch (glsl.air.getInst(inst.return_type)) {
|
||||
.@"struct" => |struct_inst| try glsl.emitGlobalStructOutputs(struct_inst),
|
||||
else => try glsl.emitGlobalScalarOutput(inst),
|
||||
}
|
||||
}
|
||||
|
||||
switch (inst.stage) {
|
||||
.compute => |workgroup_size| {
|
||||
try glsl.print("layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n", .{
|
||||
glsl.air.resolveInt(workgroup_size.x) orelse 1,
|
||||
glsl.air.resolveInt(workgroup_size.y) orelse 1,
|
||||
glsl.air.resolveInt(workgroup_size.z) orelse 1,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
try glsl.emitType(.none);
|
||||
} else {
|
||||
try glsl.emitType(inst.return_type);
|
||||
}
|
||||
|
||||
try glsl.writeAll(" ");
|
||||
if (inst.stage != .none) {
|
||||
try glsl.writeEntrypoint();
|
||||
} else {
|
||||
try glsl.writeName(inst.name);
|
||||
}
|
||||
try glsl.writeAll("(");
|
||||
|
||||
if (inst.stage == .none) {
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
var add_comma = false;
|
||||
|
||||
if (inst.params != .none) {
|
||||
for (glsl.air.refToList(inst.params)) |param_inst_idx| {
|
||||
try glsl.writeAll(if (add_comma) ",\n" else "\n");
|
||||
add_comma = true;
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitFnParam(param_inst_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try glsl.writeAll(")\n");
|
||||
|
||||
const block = glsl.air.getInst(inst.block).block;
|
||||
try glsl.writeAll("{\n");
|
||||
{
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
for (glsl.air.refToList(block)) |statement| {
|
||||
try glsl.emitStatement(statement);
|
||||
}
|
||||
}
|
||||
try glsl.writeAll("}\n");
|
||||
|
||||
glsl.entrypoint_inst = null;
|
||||
}
|
||||
|
||||
fn emitFnParam(glsl: *Glsl, inst_idx: InstIndex) !void {
|
||||
const inst = glsl.air.getInst(inst_idx).fn_param;
|
||||
|
||||
try glsl.emitType(inst.type);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(inst.name);
|
||||
}
|
||||
|
||||
fn emitStatement(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
|
||||
try glsl.writeIndent();
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.@"var" => |inst| try glsl.emitVar(inst),
|
||||
//.@"const" => |inst| try glsl.emitConst(inst),
|
||||
.block => |block| try glsl.emitBlock(block),
|
||||
// .loop => |inst| try glsl.emitLoop(inst),
|
||||
// .continuing
|
||||
.@"return" => |return_inst_idx| try glsl.emitReturn(return_inst_idx),
|
||||
// .break_if
|
||||
.@"if" => |inst| try glsl.emitIf(inst),
|
||||
// .@"while" => |inst| try glsl.emitWhile(inst),
|
||||
.@"for" => |inst| try glsl.emitFor(inst),
|
||||
// .switch
|
||||
//.discard => try glsl.emitDiscard(),
|
||||
// .@"break" => try glsl.emitBreak(),
|
||||
.@"continue" => try glsl.writeAll("continue;\n"),
|
||||
// .call => |inst| try glsl.emitCall(inst),
|
||||
.assign,
|
||||
.nil_intrinsic,
|
||||
.texture_store,
|
||||
=> {
|
||||
try glsl.emitExpr(inst_idx);
|
||||
try glsl.writeAll(";\n");
|
||||
},
|
||||
//else => |inst| std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst)}),
|
||||
else => |inst| try glsl.print("Statement: {}\n", .{inst}), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn emitVar(glsl: *Glsl, inst: Inst.Var) !void {
|
||||
const t = if (inst.type != .none) inst.type else inst.init;
|
||||
try glsl.emitType(t);
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.writeName(inst.name);
|
||||
try glsl.emitTypeSuffix(t);
|
||||
if (inst.init != .none) {
|
||||
try glsl.writeAll(" = ");
|
||||
try glsl.emitExpr(inst.init);
|
||||
}
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
|
||||
fn emitBlock(glsl: *Glsl, block: Air.RefIndex) !void {
|
||||
try glsl.writeAll("{\n");
|
||||
{
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
for (glsl.air.refToList(block)) |statement| {
|
||||
try glsl.emitStatement(statement);
|
||||
}
|
||||
}
|
||||
try glsl.writeIndent();
|
||||
try glsl.writeAll("}\n");
|
||||
}
|
||||
|
||||
fn emitReturn(glsl: *Glsl, inst_idx: InstIndex) !void {
|
||||
if (glsl.entrypoint_inst) |fn_inst| {
|
||||
if (fn_inst.return_type != .none) {
|
||||
switch (glsl.air.getInst(fn_inst.return_type)) {
|
||||
.@"struct" => |struct_inst| try glsl.emitGlobalStructReturn(struct_inst, inst_idx),
|
||||
else => try glsl.emitGlobalScalarReturn(fn_inst, inst_idx),
|
||||
}
|
||||
try glsl.writeIndent();
|
||||
}
|
||||
try glsl.writeAll("return;\n");
|
||||
} else {
|
||||
try glsl.writeAll("return");
|
||||
if (inst_idx != .none) {
|
||||
try glsl.writeAll(" ");
|
||||
try glsl.emitExpr(inst_idx);
|
||||
}
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn emitGlobalStructReturn(glsl: *Glsl, inst: Inst.Struct, inst_idx: InstIndex) !void {
|
||||
for (glsl.air.refToList(inst.members), 0..) |member_index, i| {
|
||||
const member = glsl.air.getInst(member_index).struct_member;
|
||||
|
||||
if (i > 0) try glsl.writeIndent();
|
||||
if (member.builtin) |builtin| {
|
||||
try glsl.emitBuiltin(builtin);
|
||||
} else {
|
||||
try glsl.writeName(member.name);
|
||||
}
|
||||
try glsl.writeAll(" = ");
|
||||
try glsl.emitExpr(inst_idx);
|
||||
try glsl.writeAll(".");
|
||||
try glsl.writeName(member.name);
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn emitGlobalScalarReturn(glsl: *Glsl, inst: Inst.Fn, inst_idx: InstIndex) !void {
|
||||
if (inst.return_attrs.builtin) |builtin| {
|
||||
try glsl.emitBuiltin(builtin);
|
||||
} else {
|
||||
try glsl.writeAll("main_output");
|
||||
}
|
||||
if (inst_idx != .none) {
|
||||
try glsl.writeAll(" = ");
|
||||
try glsl.emitExpr(inst_idx);
|
||||
}
|
||||
try glsl.writeAll(";\n");
|
||||
}
|
||||
|
||||
fn emitIf(glsl: *Glsl, inst: Inst.If) !void {
|
||||
try glsl.writeAll("if (");
|
||||
try glsl.emitExpr(inst.cond);
|
||||
try glsl.writeAll(")\n");
|
||||
{
|
||||
const body_inst = glsl.air.getInst(inst.body);
|
||||
if (body_inst != .block)
|
||||
glsl.enterScope();
|
||||
try glsl.emitStatement(inst.body);
|
||||
if (body_inst != .block)
|
||||
glsl.exitScope();
|
||||
}
|
||||
if (inst.@"else" != .none) {
|
||||
try glsl.writeIndent();
|
||||
try glsl.writeAll("else\n");
|
||||
try glsl.emitStatement(inst.@"else");
|
||||
}
|
||||
try glsl.writeAll("\n");
|
||||
}
|
||||
|
||||
fn emitFor(glsl: *Glsl, inst: Inst.For) !void {
|
||||
try glsl.writeAll("for (\n");
|
||||
{
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
try glsl.emitStatement(inst.init);
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitExpr(inst.cond);
|
||||
try glsl.writeAll(";\n");
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitExpr(inst.update);
|
||||
try glsl.writeAll(")\n");
|
||||
}
|
||||
try glsl.emitStatement(inst.body);
|
||||
}
|
||||
|
||||
fn emitExpr(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.var_ref => |inst| try glsl.emitVarRef(inst),
|
||||
.bool => |inst| try glsl.emitBool(inst),
|
||||
.int => |inst| try glsl.emitInt(inst),
|
||||
.float => |inst| try glsl.emitFloat(inst),
|
||||
.vector => |inst| try glsl.emitVector(inst),
|
||||
//.matrix => |inst| try glsl.emitMatrix(inst),
|
||||
.array => |inst| try glsl.emitArray(inst),
|
||||
//.nil_intrinsic => |inst| try glsl.emitNilIntrinsic(inst),
|
||||
.unary => |inst| try glsl.emitUnary(inst),
|
||||
.unary_intrinsic => |inst| try glsl.emitUnaryIntrinsic(inst),
|
||||
.binary => |inst| try glsl.emitBinary(inst),
|
||||
.binary_intrinsic => |inst| try glsl.emitBinaryIntrinsic(inst),
|
||||
.triple_intrinsic => |inst| try glsl.emitTripleIntrinsic(inst),
|
||||
.assign => |inst| try glsl.emitAssign(inst),
|
||||
.field_access => |inst| try glsl.emitFieldAccess(inst),
|
||||
.swizzle_access => |inst| try glsl.emitSwizzleAccess(inst),
|
||||
.index_access => |inst| try glsl.emitIndexAccess(inst),
|
||||
//.call => |inst| try glsl.emitCall(inst),
|
||||
//.struct_construct: StructConstruct,
|
||||
//.bitcast: Bitcast,
|
||||
//.texture_sample => |inst| try glsl.emitTextureSample(inst),
|
||||
//.texture_dimension => |inst| try glsl.emitTextureDimension(inst),
|
||||
//.texture_load => |inst| try glsl.emitTextureLoad(inst),
|
||||
//.texture_store => |inst| try glsl.emitTextureStore(inst),
|
||||
//else => |inst| std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst)}),
|
||||
else => |inst| std.debug.panic("Expr: {}", .{inst}), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn emitVarRef(glsl: *Glsl, inst_idx: InstIndex) !void {
|
||||
switch (glsl.air.getInst(inst_idx)) {
|
||||
.@"var" => |v| try glsl.writeName(v.name),
|
||||
.@"const" => |c| try glsl.writeName(c.name),
|
||||
.fn_param => |p| {
|
||||
if (p.builtin) |builtin| {
|
||||
try glsl.emitBuiltin(builtin);
|
||||
} else {
|
||||
try glsl.writeName(p.name);
|
||||
}
|
||||
},
|
||||
else => |x| std.debug.panic("VarRef: {}", .{x}), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn emitBool(glsl: *Glsl, inst: Inst.Bool) !void {
|
||||
switch (inst.value.?) {
|
||||
.literal => |lit| try glsl.print("{}", .{lit}),
|
||||
.cast => @panic("TODO"),
|
||||
}
|
||||
}
|
||||
|
||||
fn emitInt(glsl: *Glsl, inst: Inst.Int) !void {
|
||||
switch (glsl.air.getValue(Inst.Int.Value, inst.value.?)) {
|
||||
.literal => |lit| try glsl.print("{}", .{lit}),
|
||||
.cast => |cast| try glsl.emitIntCast(inst, cast),
|
||||
}
|
||||
}
|
||||
|
||||
fn emitIntCast(glsl: *Glsl, dest_type: Inst.Int, cast: Inst.ScalarCast) !void {
|
||||
try glsl.emitIntType(dest_type);
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(cast.value);
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitFloat(glsl: *Glsl, inst: Inst.Float) !void {
|
||||
switch (glsl.air.getValue(Inst.Float.Value, inst.value.?)) {
|
||||
.literal => |lit| try glsl.print("{}", .{lit}),
|
||||
.cast => |cast| try glsl.emitFloatCast(inst, cast),
|
||||
}
|
||||
}
|
||||
|
||||
fn emitFloatCast(glsl: *Glsl, dest_type: Inst.Float, cast: Inst.ScalarCast) !void {
|
||||
try glsl.emitFloatType(dest_type);
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(cast.value);
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitVector(glsl: *Glsl, inst: Inst.Vector) !void {
|
||||
try glsl.emitVectorType(inst);
|
||||
try glsl.writeAll("(");
|
||||
|
||||
const value = glsl.air.getValue(Inst.Vector.Value, inst.value.?);
|
||||
switch (value) {
|
||||
.literal => |literal| try glsl.emitVectorElems(inst.size, literal),
|
||||
.cast => |cast| try glsl.emitVectorElems(inst.size, cast.value),
|
||||
}
|
||||
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitVectorElems(glsl: *Glsl, size: Inst.Vector.Size, value: [4]InstIndex) !void {
|
||||
for (value[0..@intFromEnum(size)], 0..) |elem_inst, i| {
|
||||
try glsl.writeAll(if (i == 0) "" else ", ");
|
||||
try glsl.emitExpr(elem_inst);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitArray(glsl: *Glsl, inst: Inst.Array) !void {
|
||||
try glsl.emitType(inst.elem_type);
|
||||
try glsl.writeAll("[](");
|
||||
{
|
||||
glsl.enterScope();
|
||||
defer glsl.exitScope();
|
||||
|
||||
const value = glsl.air.refToList(inst.value.?);
|
||||
for (value, 0..) |elem_inst, i| {
|
||||
try glsl.writeAll(if (i == 0) "\n" else ",\n");
|
||||
try glsl.writeIndent();
|
||||
try glsl.emitExpr(elem_inst);
|
||||
}
|
||||
}
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitUnary(glsl: *Glsl, inst: Inst.Unary) !void {
|
||||
try glsl.writeAll(switch (inst.op) {
|
||||
.not => "!",
|
||||
.negate => "-",
|
||||
.deref => "*",
|
||||
.addr_of => @panic("unsupported"),
|
||||
});
|
||||
try glsl.emitExpr(inst.expr);
|
||||
}
|
||||
|
||||
fn emitUnaryIntrinsic(glsl: *Glsl, inst: Inst.UnaryIntrinsic) !void {
|
||||
switch (inst.op) {
|
||||
.array_length => try glsl.emitArrayLength(inst),
|
||||
else => {
|
||||
try glsl.writeAll(switch (inst.op) {
|
||||
.array_length => unreachable,
|
||||
.degrees => "radians",
|
||||
.radians => "degrees",
|
||||
.all => "all",
|
||||
.any => "any",
|
||||
.abs => "abs",
|
||||
.acos => "acos",
|
||||
.acosh => "acosh",
|
||||
.asin => "asin",
|
||||
.asinh => "asinh",
|
||||
.atan => "atan",
|
||||
.atanh => "atanh",
|
||||
.ceil => "ceil",
|
||||
.cos => "cos",
|
||||
.cosh => "cosh",
|
||||
//.count_leading_zeros => "count_leading_zeros",
|
||||
.count_one_bits => "bitCount",
|
||||
//.count_trailing_zeros => "count_trailing_zeros",
|
||||
.exp => "exp",
|
||||
.exp2 => "exp2",
|
||||
//.first_leading_bit => "first_leading_bit",
|
||||
//.first_trailing_bit => "first_trailing_bit",
|
||||
.floor => "floor",
|
||||
.fract => "fract",
|
||||
.inverse_sqrt => "inversesqrt",
|
||||
.length => "length",
|
||||
.log => "log",
|
||||
.log2 => "log2",
|
||||
//.quantize_to_F16 => "quantize_to_F16",
|
||||
.reverseBits => "bitfieldReverse",
|
||||
.round => "round",
|
||||
//.saturate => "saturate",
|
||||
.sign => "sign",
|
||||
.sin => "sin",
|
||||
.sinh => "sinh",
|
||||
.sqrt => "sqrt",
|
||||
.tan => "tan",
|
||||
.tanh => "tanh",
|
||||
.trunc => "trunc",
|
||||
.dpdx => "dFdx",
|
||||
.dpdx_coarse => "dFdxCoarse",
|
||||
.dpdx_fine => "dFdxFine",
|
||||
.dpdy => "dFdy",
|
||||
.dpdy_coarse => "dFdyCoarse",
|
||||
.dpdy_fine => "dFdyFine",
|
||||
.fwidth => "fwidth",
|
||||
.fwidth_coarse => "fwidthCoarse",
|
||||
.fwidth_fine => "fwidthFine",
|
||||
.normalize => "normalize",
|
||||
else => std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst.op)}),
|
||||
});
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(inst.expr);
|
||||
try glsl.writeAll(")");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn emitArrayLength(glsl: *Glsl, inst: Inst.UnaryIntrinsic) !void {
|
||||
switch (glsl.air.getInst(inst.expr)) {
|
||||
.unary => |un| switch (un.op) {
|
||||
.addr_of => try glsl.emitArrayLengthTarget(un.expr, 0),
|
||||
else => try glsl.print("ArrayLength (unary_op): {}", .{un.op}),
|
||||
},
|
||||
else => |array_length_expr| try glsl.print("ArrayLength (array_length_expr): {}", .{array_length_expr}),
|
||||
}
|
||||
}
|
||||
|
||||
fn emitArrayLengthTarget(glsl: *Glsl, inst_idx: InstIndex, offset: usize) error{OutOfMemory}!void {
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(inst_idx);
|
||||
try glsl.print(".length() - {}", .{offset});
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitBinary(glsl: *Glsl, inst: Inst.Binary) !void {
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(inst.lhs);
|
||||
try glsl.print(" {s} ", .{switch (inst.op) {
|
||||
.mul => "*",
|
||||
.div => "/",
|
||||
.mod => "%",
|
||||
.add => "+",
|
||||
.sub => "-",
|
||||
.shl => "<<",
|
||||
.shr => ">>",
|
||||
.@"and" => "&",
|
||||
.@"or" => "|",
|
||||
.xor => "^",
|
||||
.logical_and => "&&",
|
||||
.logical_or => "||",
|
||||
.equal => "==",
|
||||
.not_equal => "!=",
|
||||
.less_than => "<",
|
||||
.less_than_equal => "<=",
|
||||
.greater_than => ">",
|
||||
.greater_than_equal => ">=",
|
||||
}});
|
||||
try glsl.emitExpr(inst.rhs);
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitBinaryIntrinsic(glsl: *Glsl, inst: Inst.BinaryIntrinsic) !void {
|
||||
try glsl.writeAll(switch (inst.op) {
|
||||
.min => "min",
|
||||
.max => "max",
|
||||
.atan2 => "atan",
|
||||
.distance => "distance",
|
||||
.dot => "dot",
|
||||
.pow => "pow",
|
||||
.step => "step",
|
||||
});
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(inst.lhs);
|
||||
try glsl.writeAll(", ");
|
||||
try glsl.emitExpr(inst.rhs);
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitTripleIntrinsic(glsl: *Glsl, inst: Inst.TripleIntrinsic) !void {
|
||||
try glsl.writeAll(switch (inst.op) {
|
||||
.smoothstep => "smoothstep",
|
||||
.clamp => "clamp",
|
||||
.mix => "mix",
|
||||
});
|
||||
try glsl.writeAll("(");
|
||||
try glsl.emitExpr(inst.a1);
|
||||
try glsl.writeAll(", ");
|
||||
try glsl.emitExpr(inst.a2);
|
||||
try glsl.writeAll(", ");
|
||||
try glsl.emitExpr(inst.a3);
|
||||
try glsl.writeAll(")");
|
||||
}
|
||||
|
||||
fn emitAssign(glsl: *Glsl, inst: Inst.Assign) !void {
|
||||
try glsl.emitExpr(inst.lhs);
|
||||
try glsl.print(" {s}= ", .{switch (inst.mod) {
|
||||
.none => "",
|
||||
.add => "+",
|
||||
.sub => "-",
|
||||
.mul => "*",
|
||||
.div => "/",
|
||||
.mod => "%",
|
||||
.@"and" => "&",
|
||||
.@"or" => "|",
|
||||
.xor => "^",
|
||||
.shl => "<<",
|
||||
.shr => ">>",
|
||||
}});
|
||||
try glsl.emitExpr(inst.rhs);
|
||||
}
|
||||
|
||||
fn emitFieldAccess(glsl: *Glsl, inst: Inst.FieldAccess) !void {
|
||||
try glsl.emitExpr(inst.base);
|
||||
try glsl.writeAll(".");
|
||||
try glsl.writeName(inst.name);
|
||||
}
|
||||
|
||||
fn emitSwizzleAccess(glsl: *Glsl, inst: Inst.SwizzleAccess) !void {
|
||||
try glsl.emitExpr(inst.base);
|
||||
try glsl.writeAll(".");
|
||||
for (0..@intFromEnum(inst.size)) |i| {
|
||||
switch (inst.pattern[i]) {
|
||||
.x => try glsl.writeAll("x"),
|
||||
.y => try glsl.writeAll("y"),
|
||||
.z => try glsl.writeAll("z"),
|
||||
.w => try glsl.writeAll("w"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emitIndexAccess(glsl: *Glsl, inst: Inst.IndexAccess) !void {
|
||||
try glsl.emitExpr(inst.base);
|
||||
try glsl.writeAll("[");
|
||||
try glsl.emitExpr(inst.index);
|
||||
try glsl.writeAll("]");
|
||||
}
|
||||
|
||||
fn enterScope(glsl: *Glsl) void {
|
||||
glsl.indent += 4;
|
||||
}
|
||||
|
||||
fn exitScope(glsl: *Glsl) void {
|
||||
glsl.indent -= 4;
|
||||
}
|
||||
|
||||
fn writeIndent(glsl: *Glsl) !void {
|
||||
try glsl.writer.writeByteNTimes(' ', glsl.indent);
|
||||
}
|
||||
|
||||
fn writeEntrypoint(glsl: *Glsl) !void {
|
||||
try glsl.writeAll("main");
|
||||
}
|
||||
|
||||
fn writeName(glsl: *Glsl, name: Air.StringIndex) !void {
|
||||
// Suffix with index as WGSL has different scoping rules and to avoid conflicts with keywords
|
||||
const str = glsl.air.getStr(name);
|
||||
try glsl.print("{s}_{}", .{ str, @intFromEnum(name) });
|
||||
}
|
||||
|
||||
fn writeAll(glsl: *Glsl, bytes: []const u8) !void {
|
||||
try glsl.writer.writeAll(bytes);
|
||||
}
|
||||
|
||||
fn print(glsl: *Glsl, comptime format: []const u8, args: anytype) !void {
|
||||
return std.fmt.format(glsl.writer, format, args);
|
||||
}
|
||||
1189
src/sysgpu/shader/codegen/hlsl.zig
Normal file
1189
src/sysgpu/shader/codegen/hlsl.zig
Normal file
File diff suppressed because it is too large
Load diff
1220
src/sysgpu/shader/codegen/msl.zig
Normal file
1220
src/sysgpu/shader/codegen/msl.zig
Normal file
File diff suppressed because it is too large
Load diff
2808
src/sysgpu/shader/codegen/spirv.zig
Normal file
2808
src/sysgpu/shader/codegen/spirv.zig
Normal file
File diff suppressed because it is too large
Load diff
302
src/sysgpu/shader/codegen/spirv/Section.zig
Normal file
302
src/sysgpu/shader/codegen/spirv/Section.zig
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
//! Borrowed from Zig compiler codebase with changes.
|
||||
//! Licensed under LICENSE-ZIG
|
||||
//!
|
||||
//! Represents a section or subsection of instructions in a SPIR-V binary. Instructions can be append
|
||||
//! to separate sections, which can then later be merged into the final binary.
|
||||
|
||||
const std = @import("std");
|
||||
const spec = @import("spec.zig");
|
||||
const Opcode = spec.Opcode;
|
||||
const Word = spec.Word;
|
||||
|
||||
const DoubleWord = std.meta.Int(.unsigned, @bitSizeOf(Word) * 2);
|
||||
const Log2Word = std.math.Log2Int(Word);
|
||||
|
||||
const Section = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
words: std.ArrayListUnmanaged(Word) = .{},
|
||||
|
||||
pub fn deinit(section: *Section) void {
|
||||
section.words.deinit(section.allocator);
|
||||
}
|
||||
|
||||
pub fn toWords(section: Section) []Word {
|
||||
return section.words.items;
|
||||
}
|
||||
|
||||
/// Append the words from another section into this section.
|
||||
pub fn append(section: *Section, other_section: Section) !void {
|
||||
try section.words.appendSlice(section.allocator, other_section.words.items);
|
||||
}
|
||||
|
||||
/// Ensure capacity of at least `capacity` more words in this section.
|
||||
pub fn ensureUnusedCapacity(section: *Section, capacity: usize) !void {
|
||||
try section.words.ensureUnusedCapacity(section.allocator, capacity);
|
||||
}
|
||||
|
||||
/// Write an instruction and size, operands are to be inserted manually.
|
||||
pub fn emitRaw(
|
||||
section: *Section,
|
||||
opcode: Opcode,
|
||||
operand_words: usize, // opcode itself not included
|
||||
) !void {
|
||||
const word_count = 1 + operand_words;
|
||||
try section.words.ensureUnusedCapacity(section.allocator, word_count);
|
||||
section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
|
||||
}
|
||||
|
||||
pub fn emit(
|
||||
section: *Section,
|
||||
comptime opcode: spec.Opcode,
|
||||
operands: opcode.Operands(),
|
||||
) !void {
|
||||
const word_count = instructionSize(opcode, operands);
|
||||
try section.ensureUnusedCapacity(word_count);
|
||||
section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
|
||||
section.writeOperands(opcode.Operands(), operands);
|
||||
}
|
||||
|
||||
pub fn emitSpecConstantOp(
|
||||
section: *Section,
|
||||
comptime opcode: spec.Opcode,
|
||||
operands: opcode.Operands(),
|
||||
) !void {
|
||||
const word_count = operandsSize(opcode.Operands(), operands);
|
||||
try section.emitRaw(.OpSpecConstantOp, 1 + word_count);
|
||||
section.writeOperand(spec.IdRef, operands.id_result_type);
|
||||
section.writeOperand(spec.IdRef, operands.id_result);
|
||||
section.writeOperand(Opcode, opcode);
|
||||
|
||||
const fields = @typeInfo(opcode.Operands()).Struct.fields;
|
||||
// First 2 fields are always id_result_type and id_result.
|
||||
inline for (fields[2..]) |field| {
|
||||
section.writeOperand(field.type, @field(operands, field.name));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeWord(section: *Section, word: Word) void {
|
||||
section.words.appendAssumeCapacity(word);
|
||||
}
|
||||
|
||||
pub fn writeWords(section: *Section, words: []const Word) void {
|
||||
section.words.appendSliceAssumeCapacity(words);
|
||||
}
|
||||
|
||||
pub fn writeDoubleWord(section: *Section, dword: DoubleWord) void {
|
||||
section.writeWords(&[_]Word{
|
||||
@truncate(dword),
|
||||
@truncate(dword >> @bitSizeOf(Word)),
|
||||
});
|
||||
}
|
||||
|
||||
fn writeOperands(section: *Section, comptime Operands: type, operands: Operands) void {
|
||||
const fields = switch (@typeInfo(Operands)) {
|
||||
.Struct => |info| info.fields,
|
||||
.Void => return,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
inline for (fields) |field| {
|
||||
section.writeOperand(field.type, @field(operands, field.name));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void {
|
||||
switch (Operand) {
|
||||
spec.IdResult => section.writeWord(operand.id),
|
||||
spec.LiteralInteger => section.writeWord(operand),
|
||||
spec.LiteralString => section.writeString(operand),
|
||||
spec.LiteralContextDependentNumber => section.writeContextDependentNumber(operand),
|
||||
spec.LiteralExtInstInteger => section.writeWord(operand.inst),
|
||||
|
||||
// TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec json,
|
||||
// so it most likely needs to be altered into something that can actually describe the entire
|
||||
// instruction in which it is used.
|
||||
spec.LiteralSpecConstantOpInteger => section.writeWord(@intFromEnum(operand.opcode)),
|
||||
|
||||
spec.PairLiteralIntegerIdRef => section.writeWords(&.{ operand.value, operand.label.id }),
|
||||
spec.PairIdRefLiteralInteger => section.writeWords(&.{ operand.target.id, operand.member }),
|
||||
spec.PairIdRefIdRef => section.writeWords(&.{ operand[0].id, operand[1].id }),
|
||||
else => switch (@typeInfo(Operand)) {
|
||||
.Enum => section.writeWord(@intFromEnum(operand)),
|
||||
.Optional => |info| if (operand) |child| {
|
||||
section.writeOperand(info.child, child);
|
||||
},
|
||||
.Pointer => |info| {
|
||||
std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
|
||||
for (operand) |item| {
|
||||
section.writeOperand(info.child, item);
|
||||
}
|
||||
},
|
||||
.Struct => |info| {
|
||||
if (info.layout == .Packed) {
|
||||
section.writeWord(@bitCast(operand));
|
||||
} else {
|
||||
section.writeExtendedMask(Operand, operand);
|
||||
}
|
||||
},
|
||||
.Union => section.writeExtendedUnion(Operand, operand),
|
||||
else => unreachable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn writeString(section: *Section, str: []const u8) void {
|
||||
// TODO: Not actually sure whether this is correct for big-endian.
|
||||
// See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal
|
||||
const zero_terminated_len = str.len + 1;
|
||||
var i: usize = 0;
|
||||
while (i < zero_terminated_len) : (i += @sizeOf(Word)) {
|
||||
var word: Word = 0;
|
||||
|
||||
var j: usize = 0;
|
||||
while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) {
|
||||
word |= @as(Word, str[i + j]) << @as(Log2Word, @intCast(j * @bitSizeOf(u8)));
|
||||
}
|
||||
|
||||
section.words.appendAssumeCapacity(word);
|
||||
}
|
||||
}
|
||||
|
||||
fn writeContextDependentNumber(section: *Section, operand: spec.LiteralContextDependentNumber) void {
|
||||
switch (operand) {
|
||||
.int32 => |int| section.writeWord(@bitCast(int)),
|
||||
.uint32 => |int| section.writeWord(@bitCast(int)),
|
||||
.int64 => |int| section.writeDoubleWord(@bitCast(int)),
|
||||
.uint64 => |int| section.writeDoubleWord(@bitCast(int)),
|
||||
.float32 => |float| section.writeWord(@bitCast(float)),
|
||||
.float64 => |float| section.writeDoubleWord(@bitCast(float)),
|
||||
}
|
||||
}
|
||||
|
||||
fn writeExtendedMask(section: *Section, comptime Operand: type, operand: Operand) void {
|
||||
var mask: Word = 0;
|
||||
inline for (@typeInfo(Operand).Struct.fields, 0..) |field, bit| {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.Optional => if (@field(operand, field.name) != null) {
|
||||
mask |= 1 << @intCast(bit);
|
||||
},
|
||||
.Bool => if (@field(operand, field.name)) {
|
||||
mask |= 1 << @intCast(bit);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
section.writeWord(mask);
|
||||
|
||||
inline for (@typeInfo(Operand).Struct.fields) |field| {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.Optional => |info| if (@field(operand, field.name)) |child| {
|
||||
section.writeOperands(info.child, child);
|
||||
},
|
||||
.Bool => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn writeExtendedUnion(section: *Section, comptime Operand: type, operand: Operand) void {
|
||||
const tag = std.meta.activeTag(operand);
|
||||
section.writeWord(@intFromEnum(tag));
|
||||
|
||||
inline for (@typeInfo(Operand).Union.fields) |field| {
|
||||
if (@field(Operand, field.name) == tag) {
|
||||
section.writeOperands(field.type, @field(operand, field.name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize {
|
||||
return 1 + operandsSize(opcode.Operands(), operands);
|
||||
}
|
||||
|
||||
fn operandsSize(comptime Operands: type, operands: Operands) usize {
|
||||
const fields = switch (@typeInfo(Operands)) {
|
||||
.Struct => |info| info.fields,
|
||||
.Void => return 0,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
var total: usize = 0;
|
||||
inline for (fields) |field| {
|
||||
total += operandSize(field.type, @field(operands, field.name));
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
fn operandSize(comptime Operand: type, operand: Operand) usize {
|
||||
return switch (Operand) {
|
||||
spec.IdResult,
|
||||
spec.LiteralInteger,
|
||||
spec.LiteralExtInstInteger,
|
||||
=> 1,
|
||||
// Add one for zero-terminator
|
||||
spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable,
|
||||
spec.LiteralContextDependentNumber => switch (operand) {
|
||||
.int32, .uint32, .float32 => @as(usize, 1),
|
||||
.int64, .uint64, .float64 => @as(usize, 2),
|
||||
},
|
||||
|
||||
// TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec
|
||||
// json, so it most likely needs to be altered into something that can actually
|
||||
// describe the entire insturction in which it is used.
|
||||
spec.LiteralSpecConstantOpInteger => 1,
|
||||
|
||||
spec.PairLiteralIntegerIdRef,
|
||||
spec.PairIdRefLiteralInteger,
|
||||
spec.PairIdRefIdRef,
|
||||
=> 2,
|
||||
else => switch (@typeInfo(Operand)) {
|
||||
.Enum => 1,
|
||||
.Optional => |info| if (operand) |child| operandSize(info.child, child) else 0,
|
||||
.Pointer => |info| blk: {
|
||||
std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
|
||||
var total: usize = 0;
|
||||
for (operand) |item| {
|
||||
total += operandSize(info.child, item);
|
||||
}
|
||||
break :blk total;
|
||||
},
|
||||
.Struct => |info| if (info.layout == .Packed) 1 else extendedMaskSize(Operand, operand),
|
||||
.Union => extendedUnionSize(Operand, operand),
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn extendedMaskSize(comptime Operand: type, operand: Operand) usize {
|
||||
var total: usize = 0;
|
||||
var any_set = false;
|
||||
inline for (@typeInfo(Operand).Struct.fields) |field| {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.Optional => |info| if (@field(operand, field.name)) |child| {
|
||||
total += operandsSize(info.child, child);
|
||||
any_set = true;
|
||||
},
|
||||
.Bool => if (@field(operand, field.name)) {
|
||||
any_set = true;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
if (!any_set) {
|
||||
return 0;
|
||||
}
|
||||
return total + 1; // Add one for the mask itself.
|
||||
}
|
||||
|
||||
fn extendedUnionSize(comptime Operand: type, operand: Operand) usize {
|
||||
const tag = std.meta.activeTag(operand);
|
||||
inline for (@typeInfo(Operand).Union.fields) |field| {
|
||||
if (@field(Operand, field.name) == tag) {
|
||||
// Add one for the tag itself.
|
||||
return 1 + operandsSize(field.type, @field(operand, field.name));
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
7423
src/sysgpu/shader/codegen/spirv/spec.zig
Normal file
7423
src/sysgpu/shader/codegen/spirv/spec.zig
Normal file
File diff suppressed because it is too large
Load diff
528
src/sysgpu/shader/print_air.zig
Normal file
528
src/sysgpu/shader/print_air.zig
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
const std = @import("std");
|
||||
const Air = @import("Air.zig");
|
||||
|
||||
const indention_size = 2;
|
||||
|
||||
pub fn printAir(ir: Air, writer: anytype) !void {
|
||||
var p = Printer(@TypeOf(writer)){
|
||||
.ir = ir,
|
||||
.writer = writer,
|
||||
.tty = std.io.tty.Config{ .escape_codes = {} },
|
||||
};
|
||||
const globals = ir.refToList(ir.globals_index);
|
||||
for (globals) |ref| {
|
||||
try p.printInst(0, ref);
|
||||
}
|
||||
}
|
||||
|
||||
fn Printer(comptime Writer: type) type {
|
||||
return struct {
|
||||
ir: Air,
|
||||
writer: Writer,
|
||||
tty: std.io.tty.Config,
|
||||
|
||||
fn printInst(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
switch (inst) {
|
||||
.@"const" => {
|
||||
try self.printConst(indent, index);
|
||||
if (indent == 0) {
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
},
|
||||
.@"struct" => {
|
||||
try self.printStruct(0, index);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
.@"fn" => {
|
||||
std.debug.assert(indent == 0);
|
||||
try self.printFn(indent, index);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
.@"var" => try self.printVar(indent, index),
|
||||
.bool => try self.printBool(indent, index),
|
||||
.int, .float => try self.printNumber(indent, index),
|
||||
.vector => try self.printVector(indent, index),
|
||||
.matrix => try self.printMatrix(indent, index),
|
||||
.sampler_type,
|
||||
.comparison_sampler_type,
|
||||
.external_texture_type,
|
||||
=> {
|
||||
try self.tty.setColor(self.writer, .bright_magenta);
|
||||
try self.writer.print(".{s}", .{@tagName(inst)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
},
|
||||
.binary => |bin| {
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "lhs", bin.lhs);
|
||||
try self.printFieldInst(indent + 1, "rhs", bin.rhs);
|
||||
try self.instBlockEnd(indent);
|
||||
},
|
||||
.unary_intrinsic => |un| {
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "expr", un.expr);
|
||||
try self.printFieldInst(indent + 1, "res_ty", un.result_type);
|
||||
try self.printFieldEnum(indent + 1, "op", un.op);
|
||||
try self.instBlockEnd(indent);
|
||||
},
|
||||
.increase,
|
||||
.decrease,
|
||||
.loop,
|
||||
.continuing,
|
||||
.@"return",
|
||||
.break_if,
|
||||
=> |un| {
|
||||
try self.instStart(index);
|
||||
if (un != .none) {
|
||||
try self.printInst(indent, un);
|
||||
}
|
||||
try self.instEnd();
|
||||
},
|
||||
.block => try self.printBlock(indent, index),
|
||||
.@"if" => try self.printIf(indent, index),
|
||||
.@"while" => try self.printWhile(indent, index),
|
||||
.field_access => try self.printFieldAccess(indent, index),
|
||||
.index_access => try self.printIndexAccess(indent, index),
|
||||
.var_ref => |ref| {
|
||||
try self.instStart(index);
|
||||
try self.tty.setColor(self.writer, .yellow);
|
||||
try self.writer.print("{d}", .{@intFromEnum(ref)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.instEnd();
|
||||
},
|
||||
else => {
|
||||
try self.instStart(index);
|
||||
try self.writer.writeAll("TODO");
|
||||
try self.instEnd();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn printGlobalVar(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).global_var;
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.name);
|
||||
if (inst.addr_space) |addr_space| {
|
||||
try self.printFieldEnum(indent + 1, "addr_space", addr_space);
|
||||
}
|
||||
if (inst.access_mode) |access_mode| {
|
||||
try self.printFieldEnum(indent + 1, "access_mode", access_mode);
|
||||
}
|
||||
if (inst.type != .none) {
|
||||
try self.printFieldInst(indent + 1, "type", inst.type);
|
||||
}
|
||||
if (inst.expr != .none) {
|
||||
try self.printFieldInst(indent + 1, "value", inst.expr);
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printVar(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).@"var";
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.name);
|
||||
try self.printFieldEnum(indent + 1, "addr_space", inst.addr_space);
|
||||
try self.printFieldEnum(indent + 1, "access_mode", inst.access_mode);
|
||||
if (inst.type != .none) {
|
||||
try self.printFieldInst(indent + 1, "type", inst.type);
|
||||
}
|
||||
if (inst.expr != .none) {
|
||||
try self.printFieldInst(indent + 1, "value", inst.expr);
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printConst(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).@"const";
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.name);
|
||||
if (inst.type != .none) {
|
||||
try self.printFieldInst(indent + 1, "type", inst.type);
|
||||
}
|
||||
try self.printFieldInst(indent + 1, "value", inst.expr);
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printLet(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).let;
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.name);
|
||||
if (inst.type != .none) {
|
||||
try self.printFieldInst(indent + 1, "type", inst.type);
|
||||
}
|
||||
try self.printFieldInst(indent + 1, "value", inst.expr);
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printStruct(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.@"struct".name);
|
||||
try self.printFieldName(indent + 1, "members");
|
||||
try self.listStart();
|
||||
const members = self.ir.refToList(inst.@"struct".members);
|
||||
for (members) |member| {
|
||||
const member_index = member;
|
||||
const member_inst = self.ir.getInst(member_index);
|
||||
try self.printIndent(indent + 2);
|
||||
try self.instBlockStart(member_index);
|
||||
try self.printFieldString(indent + 3, "name", member_inst.struct_member.name);
|
||||
try self.printFieldInst(indent + 3, "type", member_inst.struct_member.type);
|
||||
if (member_inst.struct_member.@"align") |@"align"| {
|
||||
try self.printFieldAny(indent + 3, "align", @"align");
|
||||
}
|
||||
if (member_inst.struct_member.size) |size| {
|
||||
try self.printFieldAny(indent + 3, "size", size);
|
||||
}
|
||||
if (member_inst.struct_member.builtin) |builtin| {
|
||||
try self.printFieldAny(indent + 3, "builtin", builtin);
|
||||
}
|
||||
if (member_inst.struct_member.location) |location| {
|
||||
try self.printFieldAny(indent + 3, "location", location);
|
||||
}
|
||||
try self.instBlockEnd(indent + 2);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent + 1);
|
||||
try self.printFieldEnd();
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printFn(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldString(indent + 1, "name", inst.@"fn".name);
|
||||
|
||||
if (inst.@"fn".params != .none) {
|
||||
try self.printFieldName(indent + 1, "params");
|
||||
try self.listStart();
|
||||
const params = self.ir.refToList(inst.@"fn".params);
|
||||
for (params) |arg| {
|
||||
const arg_index = arg;
|
||||
const arg_inst = self.ir.getInst(arg_index);
|
||||
try self.printIndent(indent + 2);
|
||||
try self.instBlockStart(arg_index);
|
||||
try self.printFieldString(indent + 3, "name", arg_inst.fn_param.name);
|
||||
try self.printFieldInst(indent + 3, "type", arg_inst.fn_param.type);
|
||||
if (arg_inst.fn_param.builtin) |builtin| {
|
||||
try self.printFieldEnum(indent + 3, "builtin", builtin);
|
||||
}
|
||||
if (arg_inst.fn_param.interpolate) |interpolate| {
|
||||
try self.printFieldName(indent + 3, "interpolate");
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldEnum(indent + 4, "type", interpolate.type);
|
||||
if (interpolate.sample != .none) {
|
||||
try self.printFieldEnum(indent + 4, "sample", interpolate.sample);
|
||||
}
|
||||
try self.instBlockEnd(indent + 4);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
if (arg_inst.fn_param.location) |location| {
|
||||
try self.printFieldAny(indent + 3, "location", location);
|
||||
}
|
||||
if (arg_inst.fn_param.invariant) {
|
||||
try self.printFieldAny(indent + 3, "invariant", arg_inst.fn_param.invariant);
|
||||
}
|
||||
try self.instBlockEnd(indent + 2);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent + 1);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
if (inst.@"fn".block != .none) {
|
||||
try self.printFieldName(indent + 1, "block");
|
||||
try self.printBlock(indent + 1, inst.@"fn".block);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printBlock(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).block;
|
||||
const statements = self.ir.refToList(inst);
|
||||
try self.listStart();
|
||||
for (statements) |statement| {
|
||||
try self.printIndent(indent + 1);
|
||||
try self.printInst(indent + 1, statement);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent);
|
||||
}
|
||||
|
||||
fn printIf(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).@"if";
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "cond", inst.cond);
|
||||
if (inst.body != .none) {
|
||||
try self.printFieldInst(indent + 1, "body", inst.body);
|
||||
}
|
||||
if (inst.@"else" != .none) {
|
||||
try self.printFieldInst(indent + 1, "else", inst.@"else");
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printWhile(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index).@"while";
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "cond", inst.cond);
|
||||
if (inst.body != .none) {
|
||||
try self.printFieldInst(indent + 1, "body", inst.body);
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printBool(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
if (inst.bool.value) |value| {
|
||||
switch (value) {
|
||||
.literal => |lit| {
|
||||
try self.instStart(index);
|
||||
try self.tty.setColor(self.writer, .cyan);
|
||||
try self.writer.print("{}", .{lit});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.instEnd();
|
||||
},
|
||||
.cast => |cast| {
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "type", cast.type);
|
||||
try self.printFieldInst(indent + 1, "value", cast.type);
|
||||
try self.instBlockEnd(indent);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
try self.instStart(index);
|
||||
try self.instEnd();
|
||||
}
|
||||
}
|
||||
|
||||
fn printNumber(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.instBlockStart(index);
|
||||
switch (inst) {
|
||||
.int => |int| {
|
||||
try self.printFieldEnum(indent + 1, "type", int.type);
|
||||
if (int.value) |value| {
|
||||
switch (self.ir.getValue(Air.Inst.Int.Value, value)) {
|
||||
.literal => |lit| try self.printFieldAny(indent + 1, "value", lit),
|
||||
.cast => |cast| {
|
||||
try self.printFieldName(indent + 1, "cast");
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 2, "type", cast.type);
|
||||
try self.printFieldInst(indent + 2, "value", cast.value);
|
||||
try self.instBlockEnd(indent);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
.float => |float| {
|
||||
try self.printFieldEnum(indent + 1, "type", float.type);
|
||||
if (float.value) |value| {
|
||||
switch (self.ir.getValue(Air.Inst.Float.Value, value)) {
|
||||
.literal => |lit| try self.printFieldAny(indent + 1, "value", lit),
|
||||
.cast => |cast| {
|
||||
try self.printFieldName(indent + 1, "cast");
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 2, "type", cast.type);
|
||||
try self.printFieldInst(indent + 2, "value", cast.value);
|
||||
try self.instBlockEnd(indent);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printVector(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const vec = self.ir.getInst(index).vector;
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "type", vec.elem_type);
|
||||
if (vec.value) |value_idx| {
|
||||
if (value_idx == .none) {
|
||||
try self.printFieldAny(indent + 1, "value", "null");
|
||||
} else {
|
||||
const value = self.ir.getValue(Air.Inst.Vector.Value, value_idx);
|
||||
switch (value) {
|
||||
.literal => |lit| {
|
||||
try self.printFieldName(indent + 1, "literal");
|
||||
try self.listStart();
|
||||
for (0..@intFromEnum(vec.size)) |i| {
|
||||
try self.printIndent(indent + 2);
|
||||
try self.printInst(indent + 2, lit[i]);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent + 1);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
.cast => |cast| {
|
||||
try self.printFieldName(indent + 1, "cast");
|
||||
try self.listStart();
|
||||
for (0..@intFromEnum(vec.size)) |i| {
|
||||
try self.printIndent(indent + 2);
|
||||
try self.printInst(indent + 2, cast.value[i]);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent + 1);
|
||||
try self.printFieldEnd();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printMatrix(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const mat = self.ir.getInst(index).matrix;
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "type", mat.elem_type);
|
||||
if (mat.value) |value_idx| {
|
||||
const value = self.ir.getValue(Air.Inst.Matrix.Value, value_idx);
|
||||
try self.printFieldName(indent + 1, "value");
|
||||
try self.listStart();
|
||||
for (0..@intFromEnum(mat.cols) * @intFromEnum(mat.rows)) |i| {
|
||||
try self.printIndent(indent + 2);
|
||||
try self.printInst(indent + 2, value[i]);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.listEnd(indent + 1);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printFieldAccess(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "base", inst.field_access.base);
|
||||
try self.printFieldString(indent + 1, "name", inst.field_access.name);
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn printIndexAccess(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.instBlockStart(index);
|
||||
try self.printFieldInst(indent + 1, "base", inst.index_access.base);
|
||||
try self.printFieldInst(indent + 1, "type", inst.index_access.type);
|
||||
try self.printFieldInst(indent + 1, "index", inst.index_access.index);
|
||||
try self.instBlockEnd(indent);
|
||||
}
|
||||
|
||||
fn instStart(self: @This(), index: Air.InstIndex) !void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.tty.setColor(self.writer, .bold);
|
||||
try self.writer.print("{s}", .{@tagName(inst)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll("<");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .blue);
|
||||
try self.writer.print("{d}", .{@intFromEnum(index)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll(">");
|
||||
try self.writer.writeAll("(");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn instEnd(self: @This()) !void {
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll(")");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn instBlockStart(self: @This(), index: Air.InstIndex) !void {
|
||||
const inst = self.ir.getInst(index);
|
||||
try self.tty.setColor(self.writer, .bold);
|
||||
try self.writer.print("{s}", .{@tagName(inst)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll("<");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .blue);
|
||||
try self.writer.print("{d}", .{@intFromEnum(index)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll(">");
|
||||
try self.writer.writeAll("{\n");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn instBlockEnd(self: @This(), indent: u16) !void {
|
||||
try self.printIndent(indent);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll("}");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn listStart(self: @This()) !void {
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll("[\n");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn listEnd(self: @This(), indent: u16) !void {
|
||||
try self.printIndent(indent);
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.writeAll("]");
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn printFieldName(self: @This(), indent: u16, name: []const u8) !void {
|
||||
try self.printIndent(indent);
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.writer.print("{s}", .{name});
|
||||
try self.tty.setColor(self.writer, .dim);
|
||||
try self.writer.print(": ", .{});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
}
|
||||
|
||||
fn printFieldString(self: @This(), indent: u16, name: []const u8, value: Air.StringIndex) !void {
|
||||
try self.printFieldName(indent, name);
|
||||
try self.tty.setColor(self.writer, .green);
|
||||
try self.writer.print("'{s}'", .{self.ir.getStr(value)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
fn printFieldInst(self: @This(), indent: u16, name: []const u8, value: Air.InstIndex) !void {
|
||||
try self.printFieldName(indent, name);
|
||||
try self.printInst(indent, value);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
fn printFieldEnum(self: @This(), indent: u16, name: []const u8, value: anytype) !void {
|
||||
try self.printFieldName(indent, name);
|
||||
try self.tty.setColor(self.writer, .magenta);
|
||||
try self.writer.print(".{s}", .{@tagName(value)});
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
fn printFieldAny(self: @This(), indent: u16, name: []const u8, value: anytype) !void {
|
||||
try self.printFieldName(indent, name);
|
||||
try self.tty.setColor(self.writer, .cyan);
|
||||
if (@typeInfo(@TypeOf(value)) == .Pointer) {
|
||||
// assume string
|
||||
try self.writer.print("{s}", .{value});
|
||||
} else {
|
||||
try self.writer.print("{}", .{value});
|
||||
}
|
||||
try self.tty.setColor(self.writer, .reset);
|
||||
try self.printFieldEnd();
|
||||
}
|
||||
|
||||
fn printFieldEnd(self: @This()) !void {
|
||||
try self.writer.writeAll(",\n");
|
||||
}
|
||||
|
||||
fn printIndent(self: @This(), indent: u16) !void {
|
||||
try self.writer.writeByteNTimes(' ', indent * indention_size);
|
||||
}
|
||||
};
|
||||
}
|
||||
294
src/sysgpu/shader/test.zig
Normal file
294
src/sysgpu/shader/test.zig
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
const std = @import("std");
|
||||
const ErrorList = @import("ErrorList.zig");
|
||||
const Ast = @import("Ast.zig");
|
||||
const Air = @import("Air.zig");
|
||||
const CodeGen = @import("CodeGen.zig");
|
||||
const printAir = @import("print_air.zig").printAir;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
test "builtins" {
|
||||
const builtins = @embedFile("test/builtins.wgsl");
|
||||
try expectCodegen(builtins, "builtins.spv", .spirv, false);
|
||||
try expectCodegen(builtins, "builtins.hlsl", .hlsl, false);
|
||||
try expectCodegen(builtins, "builtins.msl", .msl, false);
|
||||
try expectCodegen(builtins, "builtins-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(if_else, "if-else.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "if-else" {
|
||||
const if_else = @embedFile("test/if-else.wgsl");
|
||||
try expectCodegen(if_else, "if-else.spv", .spirv, false);
|
||||
try expectCodegen(if_else, "if-else.hlsl", .hlsl, false);
|
||||
try expectCodegen(if_else, "if-else.msl", .msl, false);
|
||||
try expectCodegen(if_else, "if-else-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(if_else, "if-else.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "boids-sprite" {
|
||||
const boids_sprite = @embedFile("test/boids-sprite.wgsl");
|
||||
try expectCodegen(boids_sprite, "boids-sprite.spv", .spirv, false);
|
||||
try expectCodegen(boids_sprite, "boids-sprite.hlsl", .hlsl, false);
|
||||
try expectCodegen(boids_sprite, "boids-sprite.msl", .msl, false);
|
||||
try expectCodegen(boids_sprite, "boids-sprite-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(boids_sprite, "boids-sprite.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "boids-sprite-update" {
|
||||
const boids_sprite_update = @embedFile("test/boids-sprite-update.wgsl");
|
||||
try expectCodegen(boids_sprite_update, "boids-sprite-update.spv", .spirv, false);
|
||||
try expectCodegen(boids_sprite_update, "boids-sprite-update.hlsl", .hlsl, false);
|
||||
try expectCodegen(boids_sprite_update, "boids-sprite-update.msl", .msl, false);
|
||||
try expectCodegen(boids_sprite_update, "boids-sprite-update-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(boids_sprite_update, "boids-sprite-update.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "cube-map" {
|
||||
const cube_map = @embedFile("test/cube-map.wgsl");
|
||||
try expectCodegen(cube_map, "cube-map.spv", .spirv, false);
|
||||
try expectCodegen(cube_map, "cube-map.hlsl", .hlsl, false);
|
||||
try expectCodegen(cube_map, "cube-map.msl", .msl, false);
|
||||
try expectCodegen(cube_map, "cube-map-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(cube_map, "cube-map.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "fractal-cube" {
|
||||
const fractal_cube = @embedFile("test/fractal-cube.wgsl");
|
||||
try expectCodegen(fractal_cube, "fractal-cube.spv", .spirv, false);
|
||||
try expectCodegen(fractal_cube, "fractal-cube.hlsl", .hlsl, false);
|
||||
try expectCodegen(fractal_cube, "fractal-cube.msl", .msl, false);
|
||||
try expectCodegen(fractal_cube, "fractal-cube-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(fractal_cube, "fractal-cube.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "gen-texture-light" {
|
||||
const gen_texture_light = @embedFile("test/gen-texture-light.wgsl");
|
||||
try expectCodegen(gen_texture_light, "gen-texture-light.spv", .spirv, false);
|
||||
try expectCodegen(gen_texture_light, "gen-texture-light.hlsl", .hlsl, false);
|
||||
try expectCodegen(gen_texture_light, "gen-texture-light.msl", .msl, false);
|
||||
try expectCodegen(gen_texture_light, "gen-texture-light-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(gen_texture_light, "gen-texture-light.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "gen-texture-light-cube" {
|
||||
const gen_texture_light_cube = @embedFile("test/gen-texture-light-cube.wgsl");
|
||||
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.spv", .spirv, false);
|
||||
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.hlsl", .hlsl, false);
|
||||
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.msl", .msl, false);
|
||||
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "sprite2d" {
|
||||
const sprite2d = @embedFile("test/sprite2d.wgsl");
|
||||
try expectCodegen(sprite2d, "sprite2d.spv", .spirv, false);
|
||||
try expectCodegen(sprite2d, "sprite2d.hlsl", .hlsl, false);
|
||||
try expectCodegen(sprite2d, "sprite2d.msl", .msl, false);
|
||||
try expectCodegen(sprite2d, "sprite2d-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(sprite2d, "sprite2d.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "two-cubes" {
|
||||
const two_cubes = @embedFile("test/two-cubes.wgsl");
|
||||
try expectCodegen(two_cubes, "two-cubes.spv", .spirv, false);
|
||||
try expectCodegen(two_cubes, "two-cubes.hlsl", .hlsl, false);
|
||||
try expectCodegen(two_cubes, "two-cubes.msl", .msl, false);
|
||||
try expectCodegen(two_cubes, "two-cubes-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(two_cubes, "two-cubes.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "fullscreen-textured-quad" {
|
||||
const fullscreen_textured_quad = @embedFile("test/fullscreen-textured-quad.wgsl");
|
||||
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.spv", .spirv, false);
|
||||
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.hlsl", .hlsl, false);
|
||||
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.msl", .msl, false);
|
||||
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "image-blur" {
|
||||
const image_blur = @embedFile("test/image-blur.wgsl");
|
||||
try expectCodegen(image_blur, "image-blur.spv", .spirv, false);
|
||||
try expectCodegen(image_blur, "image-blur.hlsl", .hlsl, false);
|
||||
try expectCodegen(image_blur, "image-blur.msl", .msl, false);
|
||||
try expectCodegen(image_blur, "image-blur-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(image_blur, "image-blur.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "instanced-cube" {
|
||||
const instanced_cube = @embedFile("test/instanced-cube.wgsl");
|
||||
try expectCodegen(instanced_cube, "instanced-cube.spv", .spirv, false);
|
||||
try expectCodegen(instanced_cube, "instanced-cube.hlsl", .hlsl, false);
|
||||
try expectCodegen(instanced_cube, "instanced-cube.msl", .msl, false);
|
||||
// TODO
|
||||
// try expectCodegen(instanced_cube, "instanced-cube-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(instanced_cube, "instanced-cube.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "map-async" {
|
||||
const map_async = @embedFile("test/map-async.wgsl");
|
||||
try expectCodegen(map_async, "map-async.spv", .spirv, false);
|
||||
try expectCodegen(map_async, "map-async.hlsl", .hlsl, false);
|
||||
try expectCodegen(map_async, "map-async.msl", .msl, false);
|
||||
try expectCodegen(map_async, "map-async-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(map_async, "map-async.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "pbr-basic" {
|
||||
const pbr_basic = @embedFile("test/pbr-basic.wgsl");
|
||||
try expectCodegen(pbr_basic, "pbr-basic.spv", .spirv, false);
|
||||
try expectCodegen(pbr_basic, "pbr-basic.hlsl", .hlsl, false);
|
||||
try expectCodegen(pbr_basic, "pbr-basic.msl", .msl, false);
|
||||
try expectCodegen(pbr_basic, "pbr-basic-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(pbr_basic, "pbr-basic.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "pixel-post-process-normal-frag" {
|
||||
const pixel_post_process_normal_frag = @embedFile("test/pixel-post-process-normal-frag.wgsl");
|
||||
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.spv", .spirv, false);
|
||||
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.hlsl", .hlsl, false);
|
||||
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.msl", .msl, false);
|
||||
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "pixel-post-process-pixel-vert" {
|
||||
const pixel_post_process_pixel_vert = @embedFile("test/pixel-post-process-pixel-vert.wgsl");
|
||||
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.spv", .spirv, false);
|
||||
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.hlsl", .hlsl, false);
|
||||
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.msl", .msl, false);
|
||||
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "pixel-post-process-pixel-frag" {
|
||||
const pixel_post_process_pixel_frag = @embedFile("test/pixel-post-process-pixel-frag.wgsl");
|
||||
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.spv", .spirv, false);
|
||||
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.hlsl", .hlsl, false);
|
||||
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.msl", .msl, false);
|
||||
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "pixel-post-process" {
|
||||
const pixel_post_process = @embedFile("test/pixel-post-process.wgsl");
|
||||
try expectCodegen(pixel_post_process, "pixel-post-process.spv", .spirv, false);
|
||||
try expectCodegen(pixel_post_process, "pixel-post-process.hlsl", .hlsl, false);
|
||||
try expectCodegen(pixel_post_process, "pixel-post-process.msl", .msl, false);
|
||||
try expectCodegen(pixel_post_process, "pixel-post-process-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(pixel_post_process, "pixel-post-process.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "procedural-primitives" {
|
||||
const procedural_primitives = @embedFile("test/procedural-primitives.wgsl");
|
||||
try expectCodegen(procedural_primitives, "procedural-primitives.spv", .spirv, false);
|
||||
try expectCodegen(procedural_primitives, "procedural-primitives.hlsl", .hlsl, false);
|
||||
try expectCodegen(procedural_primitives, "procedural-primitives.msl", .msl, false);
|
||||
try expectCodegen(procedural_primitives, "procedural-primitives-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(procedural_primitives, "procedural-primitives.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "rotating-cube" {
|
||||
const rotating_cube = @embedFile("test/rotating-cube.wgsl");
|
||||
try expectCodegen(rotating_cube, "rotating-cube.spv", .spirv, false);
|
||||
try expectCodegen(rotating_cube, "rotating-cube.hlsl", .hlsl, false);
|
||||
try expectCodegen(rotating_cube, "rotating-cube.msl", .msl, false);
|
||||
try expectCodegen(rotating_cube, "rotating-cube-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(rotating_cube, "rotating-cube.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "triangle" {
|
||||
const triangle = @embedFile("test/triangle.wgsl");
|
||||
try expectCodegen(triangle, "triangle.spv", .spirv, false);
|
||||
try expectCodegen(triangle, "triangle.hlsl", .hlsl, false);
|
||||
try expectCodegen(triangle, "triangle.msl", .msl, false);
|
||||
try expectCodegen(triangle, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(triangle, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "fragmentDeferredRendering" {
|
||||
const fragmentDeferredRendering = @embedFile("test/fragmentDeferredRendering.wgsl");
|
||||
try expectCodegen(fragmentDeferredRendering, "fragmentDeferredRendering.spv", .spirv, false);
|
||||
try expectCodegen(fragmentDeferredRendering, "fragmentDeferredRendering.hlsl", .hlsl, false);
|
||||
try expectCodegen(fragmentDeferredRendering, "triangle.msl", .msl, false);
|
||||
try expectCodegen(fragmentDeferredRendering, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(fragmentDeferredRendering, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "fragmentGBuffersDebugView" {
|
||||
const fragmentGBuffersDebugView = @embedFile("test/fragmentGBuffersDebugView.wgsl");
|
||||
try expectCodegen(fragmentGBuffersDebugView, "fragmentGBuffersDebugView.spv", .spirv, false);
|
||||
try expectCodegen(fragmentGBuffersDebugView, "fragmentGBuffersDebugView.hlsl", .hlsl, false);
|
||||
try expectCodegen(fragmentGBuffersDebugView, "triangle.msl", .msl, false);
|
||||
try expectCodegen(fragmentGBuffersDebugView, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(fragmentGBuffersDebugView, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "fragmentWriteGBuffers" {
|
||||
const fragmentWriteGBuffers = @embedFile("test/fragmentWriteGBuffers.wgsl");
|
||||
try expectCodegen(fragmentWriteGBuffers, "fragmentWriteGBuffers.spv", .spirv, false);
|
||||
try expectCodegen(fragmentWriteGBuffers, "fragmentWriteGBuffers.hlsl", .hlsl, false);
|
||||
try expectCodegen(fragmentWriteGBuffers, "triangle.msl", .msl, false);
|
||||
try expectCodegen(fragmentWriteGBuffers, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(fragmentWriteGBuffers, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "lightUpdate" {
|
||||
const lightUpdate = @embedFile("test/lightUpdate.wgsl");
|
||||
try expectCodegen(lightUpdate, "lightUpdate.spv", .spirv, false);
|
||||
try expectCodegen(lightUpdate, "lightUpdate.hlsl", .hlsl, false);
|
||||
try expectCodegen(lightUpdate, "triangle.msl", .msl, false);
|
||||
try expectCodegen(lightUpdate, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(lightUpdate, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "vertexTextureQuad" {
|
||||
const vertexTextureQuad = @embedFile("test/vertexTextureQuad.wgsl");
|
||||
try expectCodegen(vertexTextureQuad, "vertexTextureQuad.spv", .spirv, false);
|
||||
try expectCodegen(vertexTextureQuad, "vertexTextureQuad.hlsl", .hlsl, false);
|
||||
try expectCodegen(vertexTextureQuad, "triangle.msl", .msl, false);
|
||||
try expectCodegen(vertexTextureQuad, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(vertexTextureQuad, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
test "vertexWriteGBuffers" {
|
||||
const vertexWriteGBuffers = @embedFile("test/vertexWriteGBuffers.wgsl");
|
||||
try expectCodegen(vertexWriteGBuffers, "vertexWriteGBuffers.spv", .spirv, false);
|
||||
try expectCodegen(vertexWriteGBuffers, "vertexWriteGBuffers.hlsl", .hlsl, false);
|
||||
try expectCodegen(vertexWriteGBuffers, "triangle.msl", .msl, false);
|
||||
try expectCodegen(vertexWriteGBuffers, "triangle-spirvcross.glsl", .glsl, true);
|
||||
// try expectCodegen(vertexWriteGBuffers, "triangle.glsl", .glsl, false);
|
||||
}
|
||||
|
||||
fn expectCodegen(
|
||||
source: [:0]const u8,
|
||||
comptime file_name: []const u8,
|
||||
lang: CodeGen.Language,
|
||||
use_spirv_cross: bool,
|
||||
) !void {
|
||||
var errors = try ErrorList.init(allocator);
|
||||
defer errors.deinit();
|
||||
|
||||
var tree = Ast.parse(allocator, &errors, source) catch |err| {
|
||||
if (err == error.Parsing) {
|
||||
try errors.print(source, null);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
defer tree.deinit(allocator);
|
||||
|
||||
var ir = Air.generate(allocator, &tree, &errors, null) catch |err| {
|
||||
if (err == error.AnalysisFail) {
|
||||
try errors.print(source, null);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
defer ir.deinit(allocator);
|
||||
|
||||
const out = try CodeGen.generate(allocator, &ir, lang, use_spirv_cross, .{}, null, null, null);
|
||||
defer allocator.free(out);
|
||||
|
||||
try std.fs.cwd().makePath("zig-out/shader/");
|
||||
try std.fs.cwd().writeFile("zig-out/shader/" ++ file_name, out);
|
||||
}
|
||||
90
src/sysgpu/shader/test/boids-sprite-update.wgsl
Normal file
90
src/sysgpu/shader/test/boids-sprite-update.wgsl
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
struct Particle {
|
||||
pos : vec2<f32>,
|
||||
vel : vec2<f32>,
|
||||
};
|
||||
struct SimParams {
|
||||
deltaT : f32,
|
||||
rule1Distance : f32,
|
||||
rule2Distance : f32,
|
||||
rule3Distance : f32,
|
||||
rule1Scale : f32,
|
||||
rule2Scale : f32,
|
||||
rule3Scale : f32,
|
||||
};
|
||||
struct Particles {
|
||||
particles : array<Particle>,
|
||||
};
|
||||
@binding(0) @group(0) var<uniform> params : SimParams;
|
||||
@binding(1) @group(0) var<storage, read> particlesA : Particles;
|
||||
@binding(2) @group(0) var<storage, read_write> particlesB : Particles;
|
||||
|
||||
// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
|
||||
@compute @workgroup_size(64)
|
||||
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
|
||||
var index : u32 = GlobalInvocationID.x;
|
||||
|
||||
if (index >= arrayLength(&particlesA.particles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var vPos = particlesA.particles[index].pos;
|
||||
var vVel = particlesA.particles[index].vel;
|
||||
var cMass = vec2<f32>(0.0, 0.0);
|
||||
var cVel = vec2<f32>(0.0, 0.0);
|
||||
var colVel = vec2<f32>(0.0, 0.0);
|
||||
var cMassCount : u32 = 0u;
|
||||
var cVelCount : u32 = 0u;
|
||||
var pos : vec2<f32>;
|
||||
var vel : vec2<f32>;
|
||||
|
||||
for (var i : u32 = 0u; i < arrayLength(&particlesA.particles); i = i + 1u) {
|
||||
if (i == index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pos = particlesA.particles[i].pos.xy;
|
||||
vel = particlesA.particles[i].vel.xy;
|
||||
if (distance(pos, vPos) < params.rule1Distance) {
|
||||
cMass = cMass + pos;
|
||||
cMassCount = cMassCount + 1u;
|
||||
}
|
||||
if (distance(pos, vPos) < params.rule2Distance) {
|
||||
colVel = colVel - (pos - vPos);
|
||||
}
|
||||
if (distance(pos, vPos) < params.rule3Distance) {
|
||||
cVel = cVel + vel;
|
||||
cVelCount = cVelCount + 1u;
|
||||
}
|
||||
}
|
||||
if (cMassCount > 0u) {
|
||||
var temp = f32(cMassCount);
|
||||
cMass = (cMass / vec2<f32>(temp, temp)) - vPos;
|
||||
}
|
||||
if (cVelCount > 0u) {
|
||||
var temp = f32(cVelCount);
|
||||
cVel = cVel / vec2<f32>(temp, temp);
|
||||
}
|
||||
vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) +
|
||||
(cVel * params.rule3Scale);
|
||||
|
||||
// clamp velocity for a more pleasing simulation
|
||||
vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
|
||||
// kinematic update
|
||||
vPos = vPos + (vVel * params.deltaT);
|
||||
// Wrap around boundary
|
||||
if (vPos.x < -1.0) {
|
||||
vPos.x = 1.0;
|
||||
}
|
||||
if (vPos.x > 1.0) {
|
||||
vPos.x = -1.0;
|
||||
}
|
||||
if (vPos.y < -1.0) {
|
||||
vPos.y = 1.0;
|
||||
}
|
||||
if (vPos.y > 1.0) {
|
||||
vPos.y = -1.0;
|
||||
}
|
||||
// Write back
|
||||
particlesB.particles[index].pos = vPos;
|
||||
particlesB.particles[index].vel = vVel;
|
||||
}
|
||||
15
src/sysgpu/shader/test/boids-sprite.wgsl
Normal file
15
src/sysgpu/shader/test/boids-sprite.wgsl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@vertex
|
||||
fn vert_main(@location(0) a_particlePos : vec2<f32>,
|
||||
@location(1) a_particleVel : vec2<f32>,
|
||||
@location(2) a_pos : vec2<f32>) -> @builtin(position) vec4<f32> {
|
||||
let angle = -atan2(a_particleVel.x, a_particleVel.y);
|
||||
let pos = vec2<f32>(
|
||||
(a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
|
||||
(a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
|
||||
return vec4<f32>(pos + a_particlePos, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn frag_main() -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
27
src/sysgpu/shader/test/builtins.wgsl
Normal file
27
src/sysgpu/shader/test/builtins.wgsl
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@group(0) @binding(0) var<storage> _runtime_array : array<u32>;
|
||||
|
||||
@fragment
|
||||
fn main() {
|
||||
// TODO: Add all builtins
|
||||
let _array_length = arrayLength(&_runtime_array);
|
||||
let _sin = sin(1.0);
|
||||
let _cos = cos(1.0);
|
||||
let _normalize = normalize(vec3(1.0));
|
||||
let _length = length(1.0);
|
||||
let _floor = floor(1.0);
|
||||
let _abs = abs(1.0);
|
||||
let _all = all(vec3(true));
|
||||
let _dpdx = dpdx(1.0);
|
||||
let _dpdy = dpdy(1.0);
|
||||
let _fwidth = fwidth(1.0);
|
||||
let _min = min(1.0, 1.0);
|
||||
let _max = max(1.0, 1.0);
|
||||
let _atan2 = atan2(1.0, 1.0);
|
||||
let _distance = distance(1.0, 1.0);
|
||||
let _dot = dot(vec3(1.0), vec3(1.0));
|
||||
let _pow = pow(1.0, 1.0);
|
||||
let _step = step(1.0, 1.0);
|
||||
let _mix = mix(1.0, 1.0, 1.0);
|
||||
let _clamp = clamp(1.0, 1.0, 1.0);
|
||||
let _smoothstep = smoothstep(1.0, 1.0, 1.0);
|
||||
}
|
||||
34
src/sysgpu/shader/test/cube-map.wgsl
Normal file
34
src/sysgpu/shader/test/cube-map.wgsl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
struct Uniforms {
|
||||
modelViewProjectionMatrix : mat4x4<f32>,
|
||||
}
|
||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex_main(
|
||||
@location(0) position : vec4<f32>,
|
||||
@location(1) uv : vec2<f32>
|
||||
) -> VertexOutput {
|
||||
var output : VertexOutput;
|
||||
output.Position = uniforms.modelViewProjectionMatrix * position;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@group(0) @binding(1) var mySampler: sampler;
|
||||
@group(0) @binding(2) var myTexture: texture_cube<f32>;
|
||||
|
||||
@fragment
|
||||
fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
var cubemapVec = fragPosition.xyz - vec3<f32>(0.5, 0.5, 0.5);
|
||||
return textureSample(myTexture, mySampler, cubemapVec);
|
||||
}
|
||||
35
src/sysgpu/shader/test/fractal-cube.wgsl
Normal file
35
src/sysgpu/shader/test/fractal-cube.wgsl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
struct Uniforms {
|
||||
matrix : mat4x4<f32>,
|
||||
};
|
||||
|
||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
||||
|
||||
struct VertexOut {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex fn vertex_main(
|
||||
@location(0) position : vec4<f32>,
|
||||
@location(1) uv: vec2<f32>
|
||||
) -> VertexOut {
|
||||
var output : VertexOut;
|
||||
output.Position = position * ubo.matrix;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@binding(1) @group(0) var mySampler: sampler;
|
||||
@binding(2) @group(0) var myTexture: texture_2d<f32>;
|
||||
|
||||
@fragment fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2<f32>(0.1, 0.1));
|
||||
let f = f32(length(texColor.rgb - vec3<f32>(0.5, 0.5, 0.5)) < 0.01);
|
||||
return (1.0 - f) * texColor + f * fragPosition;
|
||||
// return vec4<f32>(texColor.rgb,1.0);
|
||||
}
|
||||
83
src/sysgpu/shader/test/fragmentDeferredRendering.wgsl
Normal file
83
src/sysgpu/shader/test/fragmentDeferredRendering.wgsl
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
@group(0) @binding(0) var gBufferNormal: texture_2d<f32>;
|
||||
@group(0) @binding(1) var gBufferAlbedo: texture_2d<f32>;
|
||||
@group(0) @binding(2) var gBufferDepth: texture_depth_2d;
|
||||
|
||||
struct LightData {
|
||||
position : vec4<f32>,
|
||||
color : vec3<f32>,
|
||||
radius : f32,
|
||||
}
|
||||
struct LightsBuffer {
|
||||
lights: array<LightData>,
|
||||
}
|
||||
@group(1) @binding(0) var<storage, read> lightsBuffer: LightsBuffer;
|
||||
|
||||
struct Config {
|
||||
numLights : u32,
|
||||
}
|
||||
struct Camera {
|
||||
viewProjectionMatrix : mat4x4<f32>,
|
||||
invViewProjectionMatrix : mat4x4<f32>,
|
||||
}
|
||||
@group(1) @binding(1) var<uniform> config: Config;
|
||||
@group(1) @binding(2) var<uniform> camera: Camera;
|
||||
|
||||
fn world_from_screen_coord(coord : vec2<f32>, depth_sample: f32) -> vec3<f32> {
|
||||
// reconstruct world-space position from the screen coordinate.
|
||||
let posClip = vec4(coord.x * 2.0 - 1.0, (1.0 - coord.y) * 2.0 - 1.0, depth_sample, 1.0);
|
||||
let posWorldW = camera.invViewProjectionMatrix * posClip;
|
||||
let posWorld = posWorldW.xyz / posWorldW.www;
|
||||
return posWorld;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(
|
||||
@builtin(position) coord : vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
var result : vec3<f32>;
|
||||
|
||||
let depth = textureLoad(
|
||||
gBufferDepth,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
);
|
||||
|
||||
// Don't light the sky.
|
||||
if (depth >= 1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
let bufferSize = textureDimensions(gBufferDepth);
|
||||
let coordUV = coord.xy / vec2<f32>(bufferSize);
|
||||
let position = world_from_screen_coord(coordUV, depth);
|
||||
|
||||
let normal = textureLoad(
|
||||
gBufferNormal,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
).xyz;
|
||||
|
||||
let albedo = textureLoad(
|
||||
gBufferAlbedo,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
).rgb;
|
||||
|
||||
for (var i = 0u; i < config.numLights; i++) {
|
||||
let L = lightsBuffer.lights[i].position.xyz - position;
|
||||
let distance = length(L);
|
||||
if (distance > lightsBuffer.lights[i].radius) {
|
||||
continue;
|
||||
}
|
||||
let lambert = max(dot(normal, normalize(L)), 0.0);
|
||||
result += vec3<f32>(
|
||||
lambert * pow(1.0 - distance / lightsBuffer.lights[i].radius, 2.0) * lightsBuffer.lights[i].color * albedo
|
||||
);
|
||||
}
|
||||
|
||||
// some manual ambient
|
||||
result += vec3(0.2);
|
||||
|
||||
return vec4(result, 1.0);
|
||||
}
|
||||
44
src/sysgpu/shader/test/fragmentGBuffersDebugView.wgsl
Normal file
44
src/sysgpu/shader/test/fragmentGBuffersDebugView.wgsl
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
@group(0) @binding(0) var gBufferNormal: texture_2d<f32>;
|
||||
@group(0) @binding(1) var gBufferAlbedo: texture_2d<f32>;
|
||||
@group(0) @binding(2) var gBufferDepth: texture_depth_2d;
|
||||
|
||||
@group(1) @binding(0) var<uniform> canvas : CanvasConstants;
|
||||
|
||||
struct CanvasConstants {
|
||||
size: vec2<f32>,
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(
|
||||
@builtin(position) coord : vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
var result : vec4<f32>;
|
||||
let c = coord.xy / vec2<f32>(canvas.size.x, canvas.size.y);
|
||||
if (c.x < 0.33333) {
|
||||
let rawDepth = textureLoad(
|
||||
gBufferDepth,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
);
|
||||
// remap depth into something a bit more visible
|
||||
let depth = (1.0 - rawDepth) * 50.0;
|
||||
result = vec4(depth);
|
||||
} else if (c.x < 0.66667) {
|
||||
result = textureLoad(
|
||||
gBufferNormal,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
);
|
||||
result.x = (result.x + 1.0) * 0.5;
|
||||
result.y = (result.y + 1.0) * 0.5;
|
||||
result.z = (result.z + 1.0) * 0.5;
|
||||
} else {
|
||||
result = textureLoad(
|
||||
gBufferAlbedo,
|
||||
vec2<i32>(floor(coord.xy)),
|
||||
0
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
22
src/sysgpu/shader/test/fragmentWriteGBuffers.wgsl
Normal file
22
src/sysgpu/shader/test/fragmentWriteGBuffers.wgsl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
struct GBufferOutput {
|
||||
@location(0) normal : vec4<f32>,
|
||||
|
||||
// Textures: diffuse color, specular color, smoothness, emissive etc. could go here
|
||||
@location(1) albedo : vec4<f32>,
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(
|
||||
@location(0) fragNormal: vec3<f32>,
|
||||
@location(1) fragUV : vec2<f32>
|
||||
) -> GBufferOutput {
|
||||
// faking some kind of checkerboard texture
|
||||
let uv = floor(30.0 * fragUV);
|
||||
let c = 0.2 + 0.5 * ((uv.x + uv.y) - 2.0 * floor((uv.x + uv.y) / 2.0));
|
||||
|
||||
var output : GBufferOutput;
|
||||
output.normal = vec4(fragNormal, 1.0);
|
||||
output.albedo = vec4(c, c, c, 1.0);
|
||||
|
||||
return output;
|
||||
}
|
||||
38
src/sysgpu/shader/test/fullscreen-textured-quad.wgsl
Normal file
38
src/sysgpu/shader/test/fullscreen-textured-quad.wgsl
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
@group(0) @binding(0) var mySampler : sampler;
|
||||
@group(0) @binding(1) var myTexture : texture_2d<f32>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
|
||||
var pos = array<vec2<f32>, 6>(
|
||||
vec2<f32>( 1.0, 1.0),
|
||||
vec2<f32>( 1.0, -1.0),
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>( 1.0, 1.0),
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>(-1.0, 1.0)
|
||||
);
|
||||
|
||||
var uv = array<vec2<f32>, 6>(
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(1.0, 1.0),
|
||||
vec2<f32>(0.0, 1.0),
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(0.0, 1.0),
|
||||
vec2<f32>(0.0, 0.0)
|
||||
);
|
||||
|
||||
var output : VertexOutput;
|
||||
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
||||
output.fragUV = uv[VertexIndex];
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
|
||||
return textureSample(myTexture, mySampler, fragUV);
|
||||
}
|
||||
75
src/sysgpu/shader/test/gen-texture-light-cube.wgsl
Normal file
75
src/sysgpu/shader/test/gen-texture-light-cube.wgsl
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
struct CameraUniform {
|
||||
pos: vec4<f32>,
|
||||
view_proj: mat4x4<f32>,
|
||||
};
|
||||
|
||||
struct InstanceInput {
|
||||
@location(3) model_matrix_0: vec4<f32>,
|
||||
@location(4) model_matrix_1: vec4<f32>,
|
||||
@location(5) model_matrix_2: vec4<f32>,
|
||||
@location(6) model_matrix_3: vec4<f32>,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
@location(2) tex_coords: vec2<f32>,
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) tex_coords: vec2<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
@location(2) position: vec3<f32>,
|
||||
};
|
||||
|
||||
struct Light {
|
||||
position: vec4<f32>,
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
||||
@group(1) @binding(0) var t_diffuse: texture_2d<f32>;
|
||||
@group(1) @binding(1) var s_diffuse: sampler;
|
||||
@group(2) @binding(0) var<uniform> light: Light;
|
||||
|
||||
@vertex
|
||||
fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
|
||||
let model_matrix = mat4x4<f32>(
|
||||
instance.model_matrix_0,
|
||||
instance.model_matrix_1,
|
||||
instance.model_matrix_2,
|
||||
instance.model_matrix_3,
|
||||
);
|
||||
var out: VertexOutput;
|
||||
let world_pos = model_matrix * vec4<f32>(model.position, 1.0);
|
||||
out.position = world_pos.xyz;
|
||||
out.normal = (model_matrix * vec4<f32>(model.normal, 0.0)).xyz;
|
||||
out.clip_position = camera.view_proj * world_pos;
|
||||
out.tex_coords = model.tex_coords;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let object_color = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||
|
||||
let ambient = 0.1;
|
||||
let ambient_color = light.color.rbg * ambient;
|
||||
|
||||
let light_dir = normalize(light.position.xyz - in.position);
|
||||
let diffuse = max(dot(in.normal, light_dir), 0.0);
|
||||
let diffuse_color = light.color.rgb * diffuse;
|
||||
|
||||
let view_dir = normalize(camera.pos.xyz - in.position);
|
||||
let half_dir = normalize(view_dir + light_dir);
|
||||
let specular = pow(max(dot(in.normal, half_dir), 0.0), 32.0);
|
||||
let specular_color = light.color.rbg * specular;
|
||||
|
||||
let all = ambient_color + diffuse_color + specular_color;
|
||||
|
||||
let result = all * object_color.rgb;
|
||||
|
||||
return vec4<f32>(result, object_color.a);
|
||||
|
||||
}
|
||||
35
src/sysgpu/shader/test/gen-texture-light.wgsl
Normal file
35
src/sysgpu/shader/test/gen-texture-light.wgsl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
struct CameraUniform {
|
||||
view_pos: vec4<f32>,
|
||||
view_proj: mat4x4<f32>,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
@location(2) tex_coords: vec2<f32>,
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
struct Light {
|
||||
position: vec4<f32>,
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
||||
@group(1) @binding(0) var<uniform> light: Light;
|
||||
|
||||
@vertex
|
||||
fn vs_main(model: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let world_pos = vec4<f32>(model.position + light.position.xyz, 1.0);
|
||||
out.clip_position = camera.view_proj * world_pos;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 1.0, 1.0, 0.5);
|
||||
}
|
||||
11
src/sysgpu/shader/test/if-else.wgsl
Normal file
11
src/sysgpu/shader/test/if-else.wgsl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@fragment fn fs_main() -> @location(0) vec4<f32> {
|
||||
var dummy = false;
|
||||
if dummy {
|
||||
let dummy_var_1 = 0.0;
|
||||
return vec4<f32>(dummy_var_1, 1, 1, 1);
|
||||
} else if !dummy {
|
||||
let dummy_var_2 = 0.0;
|
||||
return vec4<f32>(dummy_var_2, 1, 1, 1);
|
||||
}
|
||||
return vec4<f32>(0.0, 1, 1, 1);
|
||||
}
|
||||
89
src/sysgpu/shader/test/image-blur.wgsl
Normal file
89
src/sysgpu/shader/test/image-blur.wgsl
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
struct Params {
|
||||
filterDim : i32,
|
||||
blockDim : u32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var samp : sampler;
|
||||
@group(0) @binding(1) var<uniform> params : Params;
|
||||
@group(1) @binding(1) var inputTex : texture_2d<f32>;
|
||||
@group(1) @binding(2) var outputTex : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
struct Flip {
|
||||
value : u32,
|
||||
}
|
||||
@group(1) @binding(3) var<uniform> flip : Flip;
|
||||
|
||||
// This shader blurs the input texture in one direction, depending on whether
|
||||
// |flip.value| is 0 or 1.
|
||||
// It does so by running (128 / 4) threads per workgroup to load 128
|
||||
// texels into 4 rows of shared memory. Each thread loads a
|
||||
// 4 x 4 block of texels to take advantage of the texture sampling
|
||||
// hardware.
|
||||
// Then, each thread computes the blur result by averaging the adjacent texel values
|
||||
// in shared memory.
|
||||
// Because we're operating on a subset of the texture, we cannot compute all of the
|
||||
// results since not all of the neighbors are available in shared memory.
|
||||
// Specifically, with 128 x 128 tiles, we can only compute and write out
|
||||
// square blocks of size 128 - (filterSize - 1). We compute the number of blocks
|
||||
// needed in Javascript and dispatch that amount.
|
||||
|
||||
var<workgroup> tile : array<array<vec3<f32>, 128>, 4>;
|
||||
|
||||
@compute @workgroup_size(32, 1, 1)
|
||||
fn main(
|
||||
@builtin(workgroup_id) WorkGroupID : vec3<u32>,
|
||||
@builtin(local_invocation_id) LocalInvocationID : vec3<u32>,
|
||||
@builtin(local_invocation_index) LocalInvocationIndex : u32
|
||||
) {
|
||||
for (var idx = LocalInvocationIndex; idx < 512u; idx+=32)
|
||||
{
|
||||
tile[idx / 128u][idx % 128u] = vec3(0.0);
|
||||
}
|
||||
workgroupBarrier();
|
||||
|
||||
// TODO - mixed vector arithmetic (vec2<u32> and vec2<i32>)
|
||||
let filterOffset = (params.filterDim - 1) / 2;
|
||||
let dims = textureDimensions(inputTex, 0);
|
||||
let baseIndex = vec2<u32>(WorkGroupID.xy * vec2(params.blockDim, 4) +
|
||||
LocalInvocationID.xy * vec2<u32>(4, 1))
|
||||
- vec2<u32>(u32(filterOffset), 0);
|
||||
|
||||
for (var r: u32 = 0; r < 4; r++) {
|
||||
for (var c: u32 = 0; c < 4; c++) {
|
||||
var loadIndex = baseIndex + vec2<u32>(c, r);
|
||||
if (flip.value != 0u) {
|
||||
loadIndex = loadIndex.yx;
|
||||
}
|
||||
|
||||
tile[r][4 * LocalInvocationID.x + c] = textureSampleLevel(
|
||||
inputTex,
|
||||
samp,
|
||||
(vec2<f32>(loadIndex) + vec2<f32>(0.25, 0.25)) / vec2<f32>(dims),
|
||||
0.0
|
||||
).rgb;
|
||||
}
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
for (var r: u32 = 0; r < 4; r++) {
|
||||
for (var c: u32 = 0; c < 4; c++) {
|
||||
var writeIndex = baseIndex + vec2<u32>(c, r);
|
||||
if (flip.value != 0) {
|
||||
writeIndex = writeIndex.yx;
|
||||
}
|
||||
|
||||
let center = u32(4 * LocalInvocationID.x) + c;
|
||||
if (center >= u32(filterOffset) &&
|
||||
center < 128 - u32(filterOffset) &&
|
||||
all(writeIndex < dims)) {
|
||||
var acc = vec3(0.0, 0.0, 0.0);
|
||||
for (var f = 0; f < params.filterDim; f++) {
|
||||
var i = i32(center) + f - filterOffset;
|
||||
acc = acc + (1.0 / f32(params.filterDim)) * tile[r][i];
|
||||
}
|
||||
textureStore(outputTex, writeIndex, vec4(acc, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/sysgpu/shader/test/instanced-cube.wgsl
Normal file
25
src/sysgpu/shader/test/instanced-cube.wgsl
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
@binding(0) @group(0) var<uniform> ubos : array<mat4x4<f32>, 16>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position_clip : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@builtin(instance_index) instanceIdx : u32,
|
||||
@location(0) position : vec4<f32>,
|
||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
||||
var output : VertexOutput;
|
||||
output.position_clip = ubos[instanceIdx] * position;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
return fragPosition;
|
||||
}
|
||||
34
src/sysgpu/shader/test/lightUpdate.wgsl
Normal file
34
src/sysgpu/shader/test/lightUpdate.wgsl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
struct LightData {
|
||||
position : vec4<f32>,
|
||||
color : vec3<f32>,
|
||||
radius : f32,
|
||||
}
|
||||
struct LightsBuffer {
|
||||
lights: array<LightData>,
|
||||
}
|
||||
@group(0) @binding(0) var<storage, read_write> lightsBuffer: LightsBuffer;
|
||||
|
||||
struct Config {
|
||||
numLights : u32,
|
||||
}
|
||||
@group(0) @binding(1) var<uniform> config: Config;
|
||||
|
||||
struct LightExtent {
|
||||
min : vec4<f32>,
|
||||
max : vec4<f32>,
|
||||
}
|
||||
@group(0) @binding(2) var<uniform> lightExtent: LightExtent;
|
||||
|
||||
@compute @workgroup_size(64, 1, 1)
|
||||
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
|
||||
var index = GlobalInvocationID.x;
|
||||
if (index >= config.numLights) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightsBuffer.lights[index].position.y = lightsBuffer.lights[index].position.y - 0.5 - 0.003 * (f32(index) - 64.0 * floor(f32(index) / 64.0));
|
||||
|
||||
if (lightsBuffer.lights[index].position.y < lightExtent.min.y) {
|
||||
lightsBuffer.lights[index].position.y = lightExtent.max.y;
|
||||
}
|
||||
}
|
||||
16
src/sysgpu/shader/test/map-async.wgsl
Normal file
16
src/sysgpu/shader/test/map-async.wgsl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
@group(0) @binding(0) var<storage, read_write> output: array<f32>;
|
||||
|
||||
@compute @workgroup_size(64, 1, 1)
|
||||
fn main(
|
||||
@builtin(global_invocation_id)
|
||||
global_id : vec3<u32>,
|
||||
|
||||
@builtin(local_invocation_id)
|
||||
local_id : vec3<u32>,
|
||||
) {
|
||||
if (global_id.x >= arrayLength(&output)) {
|
||||
return;
|
||||
}
|
||||
output[global_id.x] =
|
||||
f32(global_id.x) * 1000. + f32(local_id.x);
|
||||
}
|
||||
119
src/sysgpu/shader/test/pbr-basic.wgsl
Normal file
119
src/sysgpu/shader/test/pbr-basic.wgsl
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
@group(0) @binding(0) var<uniform> ubo : UBO;
|
||||
@group(0) @binding(1) var<uniform> uboParams : UBOShared;
|
||||
@group(0) @binding(2) var<uniform> material : MaterialParams;
|
||||
@group(0) @binding(3) var<uniform> object : ObjectParams;
|
||||
|
||||
struct VertexOut {
|
||||
@builtin(position) position_clip : vec4<f32>,
|
||||
@location(0) fragPosition : vec3<f32>,
|
||||
@location(1) fragNormal : vec3<f32>,
|
||||
}
|
||||
|
||||
struct MaterialParams {
|
||||
roughness : f32,
|
||||
metallic : f32,
|
||||
r : f32,
|
||||
g : f32,
|
||||
b : f32
|
||||
}
|
||||
|
||||
struct UBOShared {
|
||||
lights : array<vec4<f32>, 4>,
|
||||
}
|
||||
|
||||
struct UBO {
|
||||
projection : mat4x4<f32>,
|
||||
model : mat4x4<f32>,
|
||||
view : mat4x4<f32>,
|
||||
camPos : vec3<f32>,
|
||||
}
|
||||
|
||||
struct ObjectParams {
|
||||
position : vec3<f32>
|
||||
}
|
||||
|
||||
@vertex fn vertex_main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) normal : vec3<f32>
|
||||
) -> VertexOut {
|
||||
var output : VertexOut;
|
||||
var locPos = vec4<f32>(ubo.model * vec4<f32>(position, 1.0));
|
||||
output.fragPosition = locPos.xyz + object.position;
|
||||
output.fragNormal = mat3x3<f32>(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz) * normal;
|
||||
output.position_clip = ubo.projection * ubo.view * vec4<f32>(output.fragPosition, 1.0);
|
||||
return output;
|
||||
}
|
||||
|
||||
const PI : f32 = 3.14159265359;
|
||||
|
||||
fn material_color() -> vec3<f32> {
|
||||
return vec3<f32>(material.r, material.g, material.b);
|
||||
}
|
||||
|
||||
// Normal Distribution function --------------------------------------
|
||||
fn D_GGX(dotNH : f32, roughness : f32) -> f32 {
|
||||
var alpha : f32 = roughness * roughness;
|
||||
var alpha2 : f32 = alpha * alpha;
|
||||
var denom : f32 = dotNH * dotNH * (alpha2 - 1.0) + 1.0;
|
||||
return alpha2 / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Geometric Shadowing function --------------------------------------
|
||||
fn G_SchlicksmithGGX(dotNL : f32, dotNV : f32, roughness : f32) -> f32 {
|
||||
var r : f32 = roughness + 1.0;
|
||||
var k : f32 = (r * r) / 8.0;
|
||||
var GL : f32 = dotNL / (dotNL * (1.0 - k) + k);
|
||||
var GV : f32 = dotNV / (dotNV * (1.0 - k) + k);
|
||||
return GL * GV;
|
||||
}
|
||||
|
||||
// Fresnel function ----------------------------------------------------
|
||||
fn F_Schlick(cosTheta : f32, metallic : f32) -> vec3<f32> {
|
||||
var F0 : vec3<f32> = mix(vec3<f32>(0.04), material_color(), metallic);
|
||||
var F : vec3<f32> = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
return F;
|
||||
}
|
||||
|
||||
// Specular BRDF composition --------------------------------------------
|
||||
fn BRDF(L : vec3<f32>, V : vec3<f32>, N : vec3<f32>, metallic : f32, roughness : f32) -> vec3<f32> {
|
||||
var H : vec3<f32> = normalize(V + L);
|
||||
var dotNV : f32 = clamp(dot(N, V), 0.0, 1.0);
|
||||
var dotNL : f32 = clamp(dot(N, L), 0.0, 1.0);
|
||||
var dotLH : f32 = clamp(dot(L, H), 0.0, 1.0);
|
||||
var dotNH : f32 = clamp(dot(N, H), 0.0, 1.0);
|
||||
var lightColor = vec3<f32>(1.0);
|
||||
var color = vec3<f32>(0.0);
|
||||
if(dotNL > 0.0) {
|
||||
var rroughness : f32 = max(0.05, roughness);
|
||||
// D = Normal distribution (Distribution of the microfacets)
|
||||
var D : f32 = D_GGX(dotNH, roughness);
|
||||
// G = Geometric shadowing term (Microfacets shadowing)
|
||||
var G : f32 = G_SchlicksmithGGX(dotNL, dotNV, roughness);
|
||||
// F = Fresnel factor (Reflectance depending on angle of incidence)
|
||||
var F : vec3<f32> = F_Schlick(dotNV, metallic);
|
||||
var spec : vec3<f32> = (D * F * G) / (4.0 * dotNL * dotNV);
|
||||
color += spec * dotNL * lightColor;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// TODO - global variable declaration order
|
||||
@fragment fn frag_main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) normal: vec3<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
var N : vec3<f32> = normalize(normal);
|
||||
var V : vec3<f32> = normalize(ubo.camPos - position);
|
||||
var Lo = vec3<f32>(0.0);
|
||||
// Specular contribution
|
||||
for(var i: i32 = 0; i < 4; i++) {
|
||||
var L : vec3<f32> = normalize(uboParams.lights[i].xyz - position);
|
||||
Lo += BRDF(L, V, N, material.metallic, material.roughness);
|
||||
}
|
||||
// Combine with ambient
|
||||
var color : vec3<f32> = material_color() * 0.02;
|
||||
color += Lo;
|
||||
// Gamma correct
|
||||
color = pow(color, vec3<f32>(0.4545));
|
||||
return vec4<f32>(color, 1.0);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@fragment fn main(
|
||||
@location(0) normal: vec3<f32>,
|
||||
@location(1) uv: vec2<f32>,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(normal / 2 + 0.5, 1.0);
|
||||
}
|
||||
77
src/sysgpu/shader/test/pixel-post-process-pixel-frag.wgsl
Normal file
77
src/sysgpu/shader/test/pixel-post-process-pixel-frag.wgsl
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
@group(0) @binding(0)
|
||||
var draw_texture: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var draw_texture_sampler: sampler;
|
||||
|
||||
@group(0) @binding(2)
|
||||
var depth_texture: texture_depth_2d;
|
||||
@group(0) @binding(3)
|
||||
var depth_texture_sampler: sampler;
|
||||
|
||||
@group(0) @binding(4)
|
||||
var normal_texture: texture_2d<f32>;
|
||||
@group(0) @binding(5)
|
||||
var normal_texture_sampler: sampler;
|
||||
|
||||
struct View {
|
||||
@location(0) width: u32,
|
||||
@location(1) height: u32,
|
||||
@location(2) pixel_size: u32,
|
||||
}
|
||||
@group(0) @binding(6)
|
||||
var<uniform> view: View;
|
||||
|
||||
fn sample_depth(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
||||
return textureSample(
|
||||
depth_texture,
|
||||
depth_texture_sampler,
|
||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
||||
);
|
||||
}
|
||||
|
||||
fn sample_normal(uv: vec2<f32>, x: f32, y: f32) -> vec3<f32> {
|
||||
return textureSample(
|
||||
normal_texture,
|
||||
normal_texture_sampler,
|
||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
||||
).xyz;
|
||||
}
|
||||
|
||||
fn normal_indicator(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
||||
// TODO - integer promotion to float argument
|
||||
var depth_diff = sample_depth(uv, 0.0, 0.0) - sample_depth(uv, x, y);
|
||||
var dx = sample_normal(uv, 0.0, 0.0);
|
||||
var dy = sample_normal(uv, x, y);
|
||||
if (depth_diff > 0) {
|
||||
// only sample normals from closest pixel
|
||||
return 0;
|
||||
}
|
||||
return distance(dx, dy);
|
||||
}
|
||||
|
||||
@fragment fn main(
|
||||
// TODO - vertex/fragment linkage
|
||||
@location(0) uv: vec2<f32>,
|
||||
@builtin(position) position: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
// TODO - integer promotion to float argument
|
||||
var depth = sample_depth(uv, 0.0, 0.0);
|
||||
var depth_diff: f32 = 0;
|
||||
depth_diff += abs(depth - sample_depth(uv, -1.0, 0.0));
|
||||
depth_diff += abs(depth - sample_depth(uv, 1.0, 0.0));
|
||||
depth_diff += abs(depth - sample_depth(uv, 0.0, -1.0));
|
||||
depth_diff += abs(depth - sample_depth(uv, 0.0, 1.0));
|
||||
|
||||
var normal_diff: f32 = 0;
|
||||
normal_diff += normal_indicator(uv, -1.0, 0.0);
|
||||
normal_diff += normal_indicator(uv, 1.0, 0.0);
|
||||
normal_diff += normal_indicator(uv, 0.0, -1.0);
|
||||
normal_diff += normal_indicator(uv, 0.0, 1.0);
|
||||
|
||||
var color = textureSample(draw_texture, draw_texture_sampler, uv);
|
||||
if (depth_diff > 0.007) { // magic number from testing
|
||||
return color * 0.7;
|
||||
}
|
||||
// add instead of multiply so really dark pixels get brighter
|
||||
return color + (vec4<f32>(1) * step(0.1, normal_diff) * 0.7);
|
||||
}
|
||||
14
src/sysgpu/shader/test/pixel-post-process-pixel-vert.wgsl
Normal file
14
src/sysgpu/shader/test/pixel-post-process-pixel-vert.wgsl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
struct VertexOut {
|
||||
@builtin(position) position_clip: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>
|
||||
}
|
||||
|
||||
@vertex fn main(
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) uv: vec2<f32>
|
||||
) -> VertexOut {
|
||||
var output : VertexOut;
|
||||
output.position_clip = vec4<f32>(position.xy, 0.0, 1.0);
|
||||
output.uv = uv;
|
||||
return output;
|
||||
}
|
||||
27
src/sysgpu/shader/test/pixel-post-process.wgsl
Normal file
27
src/sysgpu/shader/test/pixel-post-process.wgsl
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@group(0) @binding(0) var<uniform> ubo: mat4x4<f32>;
|
||||
|
||||
struct VertexOut {
|
||||
@builtin(position) position_clip: vec4<f32>,
|
||||
@location(0) normal: vec3<f32>,
|
||||
@location(1) uv: vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex fn vertex_main(
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
@location(2) uv: vec2<f32>
|
||||
) -> VertexOut {
|
||||
var output: VertexOut;
|
||||
output.position_clip = vec4<f32>(position, 1) * ubo;
|
||||
output.normal = (vec4<f32>(normal, 0) * ubo).xyz;
|
||||
output.uv = uv;
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment fn frag_main(
|
||||
@location(0) normal: vec3<f32>,
|
||||
@location(1) uv: vec2<f32>,
|
||||
) -> @location(0) vec4<f32> {
|
||||
var color = floor((uv * 0.5 + 0.25) * 32) / 32;
|
||||
return vec4<f32>(color, 1, 1);
|
||||
}
|
||||
32
src/sysgpu/shader/test/procedural-primitives.wgsl
Normal file
32
src/sysgpu/shader/test/procedural-primitives.wgsl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
struct Uniforms {
|
||||
mvp_matrix : mat4x4<f32>,
|
||||
};
|
||||
|
||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) normal: vec3<f32>,
|
||||
};
|
||||
|
||||
@vertex fn vertex_main(
|
||||
// TODO - struct input
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = vec4<f32>(position, 1.0) * ubo.mvp_matrix;
|
||||
out.normal = normal;
|
||||
return out;
|
||||
}
|
||||
|
||||
struct FragmentOutput {
|
||||
@location(0) pixel_color: vec4<f32>
|
||||
};
|
||||
|
||||
@fragment fn frag_main(in: VertexOutput) -> FragmentOutput {
|
||||
var out : FragmentOutput;
|
||||
|
||||
out.pixel_color = vec4<f32>((in.normal + 1) / 2, 1.0);
|
||||
return out;
|
||||
}
|
||||
24
src/sysgpu/shader/test/rotating-cube.wgsl
Normal file
24
src/sysgpu/shader/test/rotating-cube.wgsl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
|
||||
struct VertexOut {
|
||||
@builtin(position) position_clip : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex fn vertex_main(
|
||||
@location(0) position : vec4<f32>,
|
||||
@location(1) uv: vec2<f32>
|
||||
) -> VertexOut {
|
||||
var output : VertexOut;
|
||||
output.position_clip = position * ubo;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
return fragPosition;
|
||||
}
|
||||
82
src/sysgpu/shader/test/sprite2d.wgsl
Normal file
82
src/sysgpu/shader/test/sprite2d.wgsl
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
struct Uniforms {
|
||||
modelViewProjectionMatrix : mat4x4<f32>,
|
||||
};
|
||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) spriteIndex : f32,
|
||||
};
|
||||
|
||||
struct Sprite {
|
||||
pos: vec2<f32>,
|
||||
size: vec2<f32>,
|
||||
world_pos: vec2<f32>,
|
||||
sheet_size: vec2<f32>,
|
||||
};
|
||||
@binding(3) @group(0) var<storage, read> sprites: array<Sprite>;
|
||||
|
||||
@vertex
|
||||
fn vertex_main(
|
||||
@builtin(vertex_index) VertexIndex : u32
|
||||
) -> VertexOutput {
|
||||
var sprite = sprites[VertexIndex / 6];
|
||||
|
||||
// Calculate the vertex position
|
||||
var positions = array<vec2<f32>, 6>(
|
||||
vec2<f32>(0.0, 0.0), // bottom-left
|
||||
vec2<f32>(0.0, 1.0), // top-left
|
||||
vec2<f32>(1.0, 0.0), // bottom-right
|
||||
vec2<f32>(1.0, 0.0), // bottom-right
|
||||
vec2<f32>(0.0, 1.0), // top-left
|
||||
vec2<f32>(1.0, 1.0), // top-right
|
||||
);
|
||||
var pos = positions[VertexIndex % 6];
|
||||
pos.x *= sprite.size.x;
|
||||
pos.y *= sprite.size.y;
|
||||
pos.x += sprite.world_pos.x;
|
||||
pos.y += sprite.world_pos.y;
|
||||
|
||||
// Calculate the UV coordinate
|
||||
var uvs = array<vec2<f32>, 6>(
|
||||
vec2<f32>(0.0, 1.0), // bottom-left
|
||||
vec2<f32>(0.0, 0.0), // top-left
|
||||
vec2<f32>(1.0, 1.0), // bottom-right
|
||||
vec2<f32>(1.0, 1.0), // bottom-right
|
||||
vec2<f32>(0.0, 0.0), // top-left
|
||||
vec2<f32>(1.0, 0.0), // top-right
|
||||
);
|
||||
var uv = uvs[VertexIndex % 6];
|
||||
uv.x *= sprite.size.x / sprite.sheet_size.x;
|
||||
uv.y *= sprite.size.y / sprite.sheet_size.y;
|
||||
uv.x += sprite.pos.x / sprite.sheet_size.x;
|
||||
uv.y += sprite.pos.y / sprite.sheet_size.y;
|
||||
|
||||
var output : VertexOutput;
|
||||
output.Position = vec4<f32>(pos.x, 0.0, pos.y, 1.0) * uniforms.modelViewProjectionMatrix;
|
||||
output.fragUV = uv;
|
||||
output.spriteIndex = f32(VertexIndex / 6);
|
||||
return output;
|
||||
}
|
||||
|
||||
@group(0) @binding(1) var spriteSampler: sampler;
|
||||
@group(0) @binding(2) var spriteTexture: texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) spriteIndex: f32
|
||||
) -> @location(0) vec4<f32> {
|
||||
var color = textureSample(spriteTexture, spriteSampler, fragUV);
|
||||
if (spriteIndex == 0.0) {
|
||||
if (color[3] > 0.0) {
|
||||
color[0] = 0.3;
|
||||
color[1] = 0.2;
|
||||
color[2] = 0.5;
|
||||
color[3] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
29
src/sysgpu/shader/test/textured-cube.wgsl
Normal file
29
src/sysgpu/shader/test/textured-cube.wgsl
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
struct Uniforms {
|
||||
modelViewProjectionMatrix : mat4x4<f32>,
|
||||
};
|
||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@location(0) position : vec4<f32>,
|
||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
||||
var output : VertexOutput;
|
||||
output.Position = position * uniforms.modelViewProjectionMatrix;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@group(0) @binding(1) var mySampler: sampler;
|
||||
@group(0) @binding(2) var myTexture: texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn frag_main(@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> {
|
||||
return textureSample(myTexture, mySampler, fragUV);
|
||||
}
|
||||
14
src/sysgpu/shader/test/triangle.wgsl
Normal file
14
src/sysgpu/shader/test/triangle.wgsl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@vertex fn vertex_main(
|
||||
@builtin(vertex_index) VertexIndex : u32
|
||||
) -> @builtin(position) vec4<f32> {
|
||||
var pos = array<vec2<f32>, 3>(
|
||||
vec2<f32>( 0.0, 0.5),
|
||||
vec2<f32>(-0.5, -0.5),
|
||||
vec2<f32>( 0.5, -0.5)
|
||||
);
|
||||
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment fn frag_main() -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
24
src/sysgpu/shader/test/two-cubes.wgsl
Normal file
24
src/sysgpu/shader/test/two-cubes.wgsl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
|
||||
struct VertexOut {
|
||||
@builtin(position) position_clip : vec4<f32>,
|
||||
@location(0) fragUV : vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex fn vertex_main(
|
||||
@location(0) position : vec4<f32>,
|
||||
@location(1) uv: vec2<f32>
|
||||
) -> VertexOut {
|
||||
var output : VertexOut;
|
||||
output.position_clip = position * ubo;
|
||||
output.fragUV = uv;
|
||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment fn frag_main(
|
||||
@location(0) fragUV: vec2<f32>,
|
||||
@location(1) fragPosition: vec4<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
return fragPosition;
|
||||
}
|
||||
11
src/sysgpu/shader/test/vertexTextureQuad.wgsl
Normal file
11
src/sysgpu/shader/test/vertexTextureQuad.wgsl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@vertex
|
||||
fn main(
|
||||
@builtin(vertex_index) VertexIndex : u32
|
||||
) -> @builtin(position) vec4<f32> {
|
||||
const pos = array(
|
||||
vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0),
|
||||
vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0),
|
||||
);
|
||||
|
||||
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
||||
}
|
||||
30
src/sysgpu/shader/test/vertexWriteGBuffers.wgsl
Normal file
30
src/sysgpu/shader/test/vertexWriteGBuffers.wgsl
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
struct Uniforms {
|
||||
modelMatrix : mat4x4<f32>,
|
||||
normalModelMatrix : mat4x4<f32>,
|
||||
}
|
||||
struct Camera {
|
||||
viewProjectionMatrix : mat4x4<f32>,
|
||||
invViewProjectionMatrix : mat4x4<f32>,
|
||||
}
|
||||
@group(0) @binding(0) var<uniform> uniforms : Uniforms;
|
||||
@group(0) @binding(1) var<uniform> camera : Camera;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) fragNormal: vec3<f32>, // normal in world space
|
||||
@location(1) fragUV: vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) normal : vec3<f32>,
|
||||
@location(2) uv : vec2<f32>
|
||||
) -> VertexOutput {
|
||||
var output : VertexOutput;
|
||||
let worldPosition = (uniforms.modelMatrix * vec4(position, 1.0)).xyz;
|
||||
output.Position = camera.viewProjectionMatrix * vec4(worldPosition, 1.0);
|
||||
output.fragNormal = normalize((uniforms.normalModelMatrix * vec4(normal, 1.0)).xyz);
|
||||
output.fragUV = uv;
|
||||
return output;
|
||||
}
|
||||
3
src/sysgpu/shader/wgsl.zig
Normal file
3
src/sysgpu/shader/wgsl.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub const Extensions = struct {
|
||||
f16: bool = false,
|
||||
};
|
||||
122
src/sysgpu/sysgpu/adapter.zig
Normal file
122
src/sysgpu/sysgpu/adapter.zig
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const dawn = @import("dawn.zig");
|
||||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStructOut = @import("main.zig").ChainedStructOut;
|
||||
const Device = @import("device.zig").Device;
|
||||
const Instance = @import("instance.zig").Instance;
|
||||
const FeatureName = @import("main.zig").FeatureName;
|
||||
const SupportedLimits = @import("main.zig").SupportedLimits;
|
||||
const RequestDeviceStatus = @import("main.zig").RequestDeviceStatus;
|
||||
const BackendType = @import("main.zig").BackendType;
|
||||
const RequestDeviceCallback = @import("main.zig").RequestDeviceCallback;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const Adapter = opaque {
|
||||
pub const Type = enum(u32) {
|
||||
discrete_gpu,
|
||||
integrated_gpu,
|
||||
cpu,
|
||||
unknown,
|
||||
|
||||
pub fn name(t: Type) []const u8 {
|
||||
return switch (t) {
|
||||
.discrete_gpu => "Discrete GPU",
|
||||
.integrated_gpu => "Integrated GPU",
|
||||
.cpu => "CPU",
|
||||
.unknown => "Unknown",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Properties = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStructOut,
|
||||
dawn_adapter_properties_power_preference: *const dawn.AdapterPropertiesPowerPreference,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
vendor_id: u32,
|
||||
vendor_name: [*:0]const u8,
|
||||
architecture: [*:0]const u8,
|
||||
device_id: u32,
|
||||
name: [*:0]const u8,
|
||||
driver_description: [*:0]const u8,
|
||||
adapter_type: Type,
|
||||
backend_type: BackendType,
|
||||
compatibility_mode: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub inline fn createDevice(adapter: *Adapter, descriptor: ?*const Device.Descriptor) ?*Device {
|
||||
return Impl.adapterCreateDevice(adapter, descriptor);
|
||||
}
|
||||
|
||||
/// Call once with null to determine the array length, and again to fetch the feature list.
|
||||
///
|
||||
/// Consider using the enumerateFeaturesOwned helper.
|
||||
pub inline fn enumerateFeatures(adapter: *Adapter, features: ?[*]FeatureName) usize {
|
||||
return Impl.adapterEnumerateFeatures(adapter, features);
|
||||
}
|
||||
|
||||
/// Enumerates the adapter features, storing the result in an allocated slice which is owned by
|
||||
/// the caller.
|
||||
pub inline fn enumerateFeaturesOwned(adapter: *Adapter, allocator: std.mem.Allocator) ![]FeatureName {
|
||||
const count = adapter.enumerateFeatures(null);
|
||||
const data = try allocator.alloc(FeatureName, count);
|
||||
_ = adapter.enumerateFeatures(data.ptr);
|
||||
return data;
|
||||
}
|
||||
|
||||
pub inline fn getInstance(adapter: *Adapter) *Instance {
|
||||
return Impl.adapterGetInstance(adapter);
|
||||
}
|
||||
|
||||
pub inline fn getLimits(adapter: *Adapter, limits: *SupportedLimits) bool {
|
||||
return Impl.adapterGetLimits(adapter, limits);
|
||||
}
|
||||
|
||||
pub inline fn getProperties(adapter: *Adapter, properties: *Adapter.Properties) void {
|
||||
Impl.adapterGetProperties(adapter, properties);
|
||||
}
|
||||
|
||||
pub inline fn hasFeature(adapter: *Adapter, feature: FeatureName) bool {
|
||||
return Impl.adapterHasFeature(adapter, feature);
|
||||
}
|
||||
|
||||
pub inline fn requestDevice(
|
||||
adapter: *Adapter,
|
||||
descriptor: ?*const Device.Descriptor,
|
||||
context: anytype,
|
||||
comptime callback: fn (
|
||||
ctx: @TypeOf(context),
|
||||
status: RequestDeviceStatus,
|
||||
device: *Device,
|
||||
message: ?[*:0]const u8,
|
||||
) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(status: RequestDeviceStatus, device: *Device, message: ?[*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
|
||||
callback(
|
||||
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
|
||||
status,
|
||||
device,
|
||||
message,
|
||||
);
|
||||
}
|
||||
};
|
||||
Impl.adapterRequestDevice(adapter, descriptor, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn reference(adapter: *Adapter) void {
|
||||
Impl.adapterReference(adapter);
|
||||
}
|
||||
|
||||
pub inline fn release(adapter: *Adapter) void {
|
||||
Impl.adapterRelease(adapter);
|
||||
}
|
||||
};
|
||||
|
||||
test "Adapter.Type name" {
|
||||
try testing.expectEqualStrings("Discrete GPU", Adapter.Type.discrete_gpu.name());
|
||||
}
|
||||
90
src/sysgpu/sysgpu/bind_group.zig
Normal file
90
src/sysgpu/sysgpu/bind_group.zig
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const Sampler = @import("sampler.zig").Sampler;
|
||||
const TextureView = @import("texture_view.zig").TextureView;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
|
||||
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const BindGroup = opaque {
|
||||
pub const Entry = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
external_texture_binding_entry: *const ExternalTexture.BindingEntry,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
binding: u32,
|
||||
buffer: ?*Buffer = null,
|
||||
offset: u64 = 0,
|
||||
size: u64,
|
||||
elem_size: u32 = 0, // TEMP - using StructuredBuffer until we switch to DXC for templatized raw buffers
|
||||
sampler: ?*Sampler = null,
|
||||
texture_view: ?*TextureView = null,
|
||||
|
||||
/// Helper to create a buffer BindGroup.Entry.
|
||||
pub fn buffer(binding: u32, buf: *Buffer, offset: u64, size: u64, elem_size: u32) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.buffer = buf,
|
||||
.offset = offset,
|
||||
.size = size,
|
||||
.elem_size = elem_size,
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper to create a sampler BindGroup.Entry.
|
||||
pub fn sampler(binding: u32, _sampler: *Sampler) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.sampler = _sampler,
|
||||
.size = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper to create a texture view BindGroup.Entry.
|
||||
pub fn textureView(binding: u32, texture_view: *TextureView) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.texture_view = texture_view,
|
||||
.size = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
layout: *BindGroupLayout,
|
||||
entry_count: usize = 0,
|
||||
entries: ?[*]const Entry = null,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
layout: *BindGroupLayout,
|
||||
entries: ?[]const Entry = null,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.layout = v.layout,
|
||||
.entry_count = if (v.entries) |e| e.len else 0,
|
||||
.entries = if (v.entries) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn setLabel(bind_group: *BindGroup, label: [*:0]const u8) void {
|
||||
Impl.bindGroupSetLabel(bind_group, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(bind_group: *BindGroup) void {
|
||||
Impl.bindGroupReference(bind_group);
|
||||
}
|
||||
|
||||
pub inline fn release(bind_group: *BindGroup) void {
|
||||
Impl.bindGroupRelease(bind_group);
|
||||
}
|
||||
};
|
||||
131
src/sysgpu/sysgpu/bind_group_layout.zig
Normal file
131
src/sysgpu/sysgpu/bind_group_layout.zig
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ShaderStageFlags = @import("main.zig").ShaderStageFlags;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const Sampler = @import("sampler.zig").Sampler;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
const TextureView = @import("texture_view.zig").TextureView;
|
||||
const StorageTextureBindingLayout = @import("main.zig").StorageTextureBindingLayout;
|
||||
const StorageTextureAccess = @import("main.zig").StorageTextureAccess;
|
||||
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const BindGroupLayout = opaque {
|
||||
pub const Entry = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
external_texture_binding_layout: *const ExternalTexture.BindingLayout,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
binding: u32,
|
||||
visibility: ShaderStageFlags,
|
||||
buffer: Buffer.BindingLayout = .{},
|
||||
sampler: Sampler.BindingLayout = .{},
|
||||
texture: Texture.BindingLayout = .{},
|
||||
storage_texture: StorageTextureBindingLayout = .{},
|
||||
|
||||
/// Helper to create a buffer BindGroupLayout.Entry.
|
||||
pub fn buffer(
|
||||
binding: u32,
|
||||
visibility: ShaderStageFlags,
|
||||
binding_type: Buffer.BindingType,
|
||||
has_dynamic_offset: bool,
|
||||
min_binding_size: u64,
|
||||
) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.visibility = visibility,
|
||||
.buffer = .{
|
||||
.type = binding_type,
|
||||
.has_dynamic_offset = Bool32.from(has_dynamic_offset),
|
||||
.min_binding_size = min_binding_size,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper to create a sampler BindGroupLayout.Entry.
|
||||
pub fn sampler(
|
||||
binding: u32,
|
||||
visibility: ShaderStageFlags,
|
||||
binding_type: Sampler.BindingType,
|
||||
) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.visibility = visibility,
|
||||
.sampler = .{ .type = binding_type },
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper to create a texture BindGroupLayout.Entry.
|
||||
pub fn texture(
|
||||
binding: u32,
|
||||
visibility: ShaderStageFlags,
|
||||
sample_type: Texture.SampleType,
|
||||
view_dimension: TextureView.Dimension,
|
||||
multisampled: bool,
|
||||
) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.visibility = visibility,
|
||||
.texture = .{
|
||||
.sample_type = sample_type,
|
||||
.view_dimension = view_dimension,
|
||||
.multisampled = Bool32.from(multisampled),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper to create a storage texture BindGroupLayout.Entry.
|
||||
pub fn storageTexture(
|
||||
binding: u32,
|
||||
visibility: ShaderStageFlags,
|
||||
access: StorageTextureAccess,
|
||||
format: Texture.Format,
|
||||
view_dimension: TextureView.Dimension,
|
||||
) Entry {
|
||||
return .{
|
||||
.binding = binding,
|
||||
.visibility = visibility,
|
||||
.storage_texture = .{
|
||||
.access = access,
|
||||
.format = format,
|
||||
.view_dimension = view_dimension,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
entry_count: usize = 0,
|
||||
entries: ?[*]const Entry = null,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
entries: ?[]const Entry = null,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.entry_count = if (v.entries) |e| e.len else 0,
|
||||
.entries = if (v.entries) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn setLabel(bind_group_layout: *BindGroupLayout, label: [*:0]const u8) void {
|
||||
Impl.bindGroupLayoutSetLabel(bind_group_layout, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(bind_group_layout: *BindGroupLayout) void {
|
||||
Impl.bindGroupLayoutReference(bind_group_layout);
|
||||
}
|
||||
|
||||
pub inline fn release(bind_group_layout: *BindGroupLayout) void {
|
||||
Impl.bindGroupLayoutRelease(bind_group_layout);
|
||||
}
|
||||
};
|
||||
166
src/sysgpu/sysgpu/buffer.zig
Normal file
166
src/sysgpu/sysgpu/buffer.zig
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
const std = @import("std");
|
||||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const dawn = @import("dawn.zig");
|
||||
const MapModeFlags = @import("main.zig").MapModeFlags;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const Buffer = opaque {
|
||||
pub const MapCallback = *const fn (status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void;
|
||||
|
||||
pub const BindingType = enum(u32) {
|
||||
undefined = 0x00000000,
|
||||
uniform = 0x00000001,
|
||||
storage = 0x00000002,
|
||||
read_only_storage = 0x00000003,
|
||||
};
|
||||
|
||||
pub const MapState = enum(u32) {
|
||||
unmapped = 0x00000000,
|
||||
pending = 0x00000001,
|
||||
mapped = 0x00000002,
|
||||
};
|
||||
|
||||
pub const MapAsyncStatus = enum(u32) {
|
||||
success = 0x00000000,
|
||||
validation_error = 0x00000001,
|
||||
unknown = 0x00000002,
|
||||
device_lost = 0x00000003,
|
||||
destroyed_before_callback = 0x00000004,
|
||||
unmapped_before_callback = 0x00000005,
|
||||
mapping_already_pending = 0x00000006,
|
||||
offset_out_of_range = 0x00000007,
|
||||
size_out_of_range = 0x00000008,
|
||||
};
|
||||
|
||||
pub const UsageFlags = packed struct(u32) {
|
||||
map_read: bool = false,
|
||||
map_write: bool = false,
|
||||
copy_src: bool = false,
|
||||
copy_dst: bool = false,
|
||||
index: bool = false,
|
||||
vertex: bool = false,
|
||||
uniform: bool = false,
|
||||
storage: bool = false,
|
||||
indirect: bool = false,
|
||||
query_resolve: bool = false,
|
||||
|
||||
_padding: u22 = 0,
|
||||
|
||||
comptime {
|
||||
std.debug.assert(
|
||||
@sizeOf(@This()) == @sizeOf(u32) and
|
||||
@bitSizeOf(@This()) == @bitSizeOf(u32),
|
||||
);
|
||||
}
|
||||
|
||||
pub const none = UsageFlags{};
|
||||
|
||||
pub fn equal(a: UsageFlags, b: UsageFlags) bool {
|
||||
return @as(u10, @truncate(@as(u32, @bitCast(a)))) == @as(u10, @truncate(@as(u32, @bitCast(b))));
|
||||
}
|
||||
};
|
||||
|
||||
pub const BindingLayout = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
type: BindingType = .undefined,
|
||||
has_dynamic_offset: Bool32 = .false,
|
||||
min_binding_size: u64 = 0,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
dawn_buffer_descriptor_error_info_from_wire_client: *const dawn.BufferDescriptorErrorInfoFromWireClient,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
usage: UsageFlags,
|
||||
size: u64,
|
||||
mapped_at_creation: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub inline fn destroy(buffer: *Buffer) void {
|
||||
Impl.bufferDestroy(buffer);
|
||||
}
|
||||
|
||||
pub inline fn getMapState(buffer: *Buffer) MapState {
|
||||
return Impl.bufferGetMapState(buffer);
|
||||
}
|
||||
|
||||
/// Default `offset_bytes`: 0
|
||||
/// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range)
|
||||
pub inline fn getConstMappedRange(
|
||||
buffer: *Buffer,
|
||||
comptime T: type,
|
||||
offset_bytes: usize,
|
||||
len: usize,
|
||||
) ?[]const T {
|
||||
const size = @sizeOf(T) * len;
|
||||
const data = Impl.bufferGetConstMappedRange(
|
||||
buffer,
|
||||
offset_bytes,
|
||||
size + size % 4,
|
||||
);
|
||||
return if (data) |d| @as([*]const T, @ptrCast(@alignCast(d)))[0..len] else null;
|
||||
}
|
||||
|
||||
/// Default `offset_bytes`: 0
|
||||
/// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range)
|
||||
pub inline fn getMappedRange(
|
||||
buffer: *Buffer,
|
||||
comptime T: type,
|
||||
offset_bytes: usize,
|
||||
len: usize,
|
||||
) ?[]T {
|
||||
const size = @sizeOf(T) * len;
|
||||
const data = Impl.bufferGetMappedRange(
|
||||
buffer,
|
||||
offset_bytes,
|
||||
size + size % 4,
|
||||
);
|
||||
return if (data) |d| @as([*]T, @ptrCast(@alignCast(d)))[0..len] else null;
|
||||
}
|
||||
|
||||
pub inline fn getSize(buffer: *Buffer) u64 {
|
||||
return Impl.bufferGetSize(buffer);
|
||||
}
|
||||
|
||||
pub inline fn getUsage(buffer: *Buffer) Buffer.UsageFlags {
|
||||
return Impl.bufferGetUsage(buffer);
|
||||
}
|
||||
|
||||
pub inline fn mapAsync(
|
||||
buffer: *Buffer,
|
||||
mode: MapModeFlags,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
context: anytype,
|
||||
comptime callback: fn (ctx: @TypeOf(context), status: MapAsyncStatus) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void {
|
||||
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status);
|
||||
}
|
||||
};
|
||||
Impl.bufferMapAsync(buffer, mode, offset, size, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(buffer: *Buffer, label: [*:0]const u8) void {
|
||||
Impl.bufferSetLabel(buffer, label);
|
||||
}
|
||||
|
||||
pub inline fn unmap(buffer: *Buffer) void {
|
||||
Impl.bufferUnmap(buffer);
|
||||
}
|
||||
|
||||
pub inline fn reference(buffer: *Buffer) void {
|
||||
Impl.bufferReference(buffer);
|
||||
}
|
||||
|
||||
pub inline fn release(buffer: *Buffer) void {
|
||||
Impl.bufferRelease(buffer);
|
||||
}
|
||||
};
|
||||
21
src/sysgpu/sysgpu/command_buffer.zig
Normal file
21
src/sysgpu/sysgpu/command_buffer.zig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const CommandBuffer = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub inline fn setLabel(command_buffer: *CommandBuffer, label: [*:0]const u8) void {
|
||||
Impl.commandBufferSetLabel(command_buffer, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(command_buffer: *CommandBuffer) void {
|
||||
Impl.commandBufferReference(command_buffer);
|
||||
}
|
||||
|
||||
pub inline fn release(command_buffer: *CommandBuffer) void {
|
||||
Impl.commandBufferRelease(command_buffer);
|
||||
}
|
||||
};
|
||||
111
src/sysgpu/sysgpu/command_encoder.zig
Normal file
111
src/sysgpu/sysgpu/command_encoder.zig
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
const std = @import("std");
|
||||
const ComputePassEncoder = @import("compute_pass_encoder.zig").ComputePassEncoder;
|
||||
const RenderPassEncoder = @import("render_pass_encoder.zig").RenderPassEncoder;
|
||||
const CommandBuffer = @import("command_buffer.zig").CommandBuffer;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const QuerySet = @import("query_set.zig").QuerySet;
|
||||
const RenderPassDescriptor = @import("main.zig").RenderPassDescriptor;
|
||||
const ComputePassDescriptor = @import("main.zig").ComputePassDescriptor;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ImageCopyBuffer = @import("main.zig").ImageCopyBuffer;
|
||||
const ImageCopyTexture = @import("main.zig").ImageCopyTexture;
|
||||
const Extent3D = @import("main.zig").Extent3D;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const dawn = @import("dawn.zig");
|
||||
|
||||
pub const CommandEncoder = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
dawn_encoder_internal_usage_descriptor: *const dawn.EncoderInternalUsageDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub inline fn beginComputePass(command_encoder: *CommandEncoder, descriptor: ?*const ComputePassDescriptor) *ComputePassEncoder {
|
||||
return Impl.commandEncoderBeginComputePass(command_encoder, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn beginRenderPass(command_encoder: *CommandEncoder, descriptor: *const RenderPassDescriptor) *RenderPassEncoder {
|
||||
return Impl.commandEncoderBeginRenderPass(command_encoder, descriptor);
|
||||
}
|
||||
|
||||
/// Default `offset`: 0
|
||||
/// Default `size`: `gpu.whole_size`
|
||||
pub inline fn clearBuffer(command_encoder: *CommandEncoder, buffer: *Buffer, offset: u64, size: u64) void {
|
||||
Impl.commandEncoderClearBuffer(command_encoder, buffer, offset, size);
|
||||
}
|
||||
|
||||
pub inline fn copyBufferToBuffer(command_encoder: *CommandEncoder, source: *Buffer, source_offset: u64, destination: *Buffer, destination_offset: u64, size: u64) void {
|
||||
Impl.commandEncoderCopyBufferToBuffer(command_encoder, source, source_offset, destination, destination_offset, size);
|
||||
}
|
||||
|
||||
pub inline fn copyBufferToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyBuffer, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void {
|
||||
Impl.commandEncoderCopyBufferToTexture(command_encoder, source, destination, copy_size);
|
||||
}
|
||||
|
||||
pub inline fn copyTextureToBuffer(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyBuffer, copy_size: *const Extent3D) void {
|
||||
Impl.commandEncoderCopyTextureToBuffer(command_encoder, source, destination, copy_size);
|
||||
}
|
||||
|
||||
pub inline fn copyTextureToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void {
|
||||
Impl.commandEncoderCopyTextureToTexture(command_encoder, source, destination, copy_size);
|
||||
}
|
||||
|
||||
pub inline fn finish(command_encoder: *CommandEncoder, descriptor: ?*const CommandBuffer.Descriptor) *CommandBuffer {
|
||||
return Impl.commandEncoderFinish(command_encoder, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn injectValidationError(command_encoder: *CommandEncoder, message: [*:0]const u8) void {
|
||||
Impl.commandEncoderInjectValidationError(command_encoder, message);
|
||||
}
|
||||
|
||||
pub inline fn insertDebugMarker(command_encoder: *CommandEncoder, marker_label: [*:0]const u8) void {
|
||||
Impl.commandEncoderInsertDebugMarker(command_encoder, marker_label);
|
||||
}
|
||||
|
||||
pub inline fn popDebugGroup(command_encoder: *CommandEncoder) void {
|
||||
Impl.commandEncoderPopDebugGroup(command_encoder);
|
||||
}
|
||||
|
||||
pub inline fn pushDebugGroup(command_encoder: *CommandEncoder, group_label: [*:0]const u8) void {
|
||||
Impl.commandEncoderPushDebugGroup(command_encoder, group_label);
|
||||
}
|
||||
|
||||
pub inline fn resolveQuerySet(command_encoder: *CommandEncoder, query_set: *QuerySet, first_query: u32, query_count: u32, destination: *Buffer, destination_offset: u64) void {
|
||||
Impl.commandEncoderResolveQuerySet(command_encoder, query_set, first_query, query_count, destination, destination_offset);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(command_encoder: *CommandEncoder, label: [*:0]const u8) void {
|
||||
Impl.commandEncoderSetLabel(command_encoder, label);
|
||||
}
|
||||
|
||||
pub inline fn writeBuffer(
|
||||
command_encoder: *CommandEncoder,
|
||||
buffer: *Buffer,
|
||||
buffer_offset_bytes: u64,
|
||||
data_slice: anytype,
|
||||
) void {
|
||||
Impl.commandEncoderWriteBuffer(
|
||||
command_encoder,
|
||||
buffer,
|
||||
buffer_offset_bytes,
|
||||
@as([*]const u8, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
|
||||
@as(u64, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn writeTimestamp(command_encoder: *CommandEncoder, query_set: *QuerySet, query_index: u32) void {
|
||||
Impl.commandEncoderWriteTimestamp(command_encoder, query_set, query_index);
|
||||
}
|
||||
|
||||
pub inline fn reference(command_encoder: *CommandEncoder) void {
|
||||
Impl.commandEncoderReference(command_encoder);
|
||||
}
|
||||
|
||||
pub inline fn release(command_encoder: *CommandEncoder) void {
|
||||
Impl.commandEncoderRelease(command_encoder);
|
||||
}
|
||||
};
|
||||
64
src/sysgpu/sysgpu/compute_pass_encoder.zig
Normal file
64
src/sysgpu/sysgpu/compute_pass_encoder.zig
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const BindGroup = @import("bind_group.zig").BindGroup;
|
||||
const ComputePipeline = @import("compute_pipeline.zig").ComputePipeline;
|
||||
const QuerySet = @import("query_set.zig").QuerySet;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const ComputePassEncoder = opaque {
|
||||
/// Default `workgroup_count_y`: 1
|
||||
/// Default `workgroup_count_z`: 1
|
||||
pub inline fn dispatchWorkgroups(compute_pass_encoder: *ComputePassEncoder, workgroup_count_x: u32, workgroup_count_y: u32, workgroup_count_z: u32) void {
|
||||
Impl.computePassEncoderDispatchWorkgroups(compute_pass_encoder, workgroup_count_x, workgroup_count_y, workgroup_count_z);
|
||||
}
|
||||
|
||||
pub inline fn dispatchWorkgroupsIndirect(compute_pass_encoder: *ComputePassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
|
||||
Impl.computePassEncoderDispatchWorkgroupsIndirect(compute_pass_encoder, indirect_buffer, indirect_offset);
|
||||
}
|
||||
|
||||
pub inline fn end(compute_pass_encoder: *ComputePassEncoder) void {
|
||||
Impl.computePassEncoderEnd(compute_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn insertDebugMarker(compute_pass_encoder: *ComputePassEncoder, marker_label: [*:0]const u8) void {
|
||||
Impl.computePassEncoderInsertDebugMarker(compute_pass_encoder, marker_label);
|
||||
}
|
||||
|
||||
pub inline fn popDebugGroup(compute_pass_encoder: *ComputePassEncoder) void {
|
||||
Impl.computePassEncoderPopDebugGroup(compute_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn pushDebugGroup(compute_pass_encoder: *ComputePassEncoder, group_label: [*:0]const u8) void {
|
||||
Impl.computePassEncoderPushDebugGroup(compute_pass_encoder, group_label);
|
||||
}
|
||||
|
||||
/// Default `dynamic_offsets`: null
|
||||
pub inline fn setBindGroup(compute_pass_encoder: *ComputePassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
|
||||
Impl.computePassEncoderSetBindGroup(
|
||||
compute_pass_encoder,
|
||||
group_index,
|
||||
group,
|
||||
if (dynamic_offsets) |v| v.len else 0,
|
||||
if (dynamic_offsets) |v| v.ptr else null,
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(compute_pass_encoder: *ComputePassEncoder, label: [*:0]const u8) void {
|
||||
Impl.computePassEncoderSetLabel(compute_pass_encoder, label);
|
||||
}
|
||||
|
||||
pub inline fn setPipeline(compute_pass_encoder: *ComputePassEncoder, pipeline: *ComputePipeline) void {
|
||||
Impl.computePassEncoderSetPipeline(compute_pass_encoder, pipeline);
|
||||
}
|
||||
|
||||
pub inline fn writeTimestamp(compute_pass_encoder: *ComputePassEncoder, query_set: *QuerySet, query_index: u32) void {
|
||||
Impl.computePassEncoderWriteTimestamp(compute_pass_encoder, query_set, query_index);
|
||||
}
|
||||
|
||||
pub inline fn reference(compute_pass_encoder: *ComputePassEncoder) void {
|
||||
Impl.computePassEncoderReference(compute_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn release(compute_pass_encoder: *ComputePassEncoder) void {
|
||||
Impl.computePassEncoderRelease(compute_pass_encoder);
|
||||
}
|
||||
};
|
||||
30
src/sysgpu/sysgpu/compute_pipeline.zig
Normal file
30
src/sysgpu/sysgpu/compute_pipeline.zig
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ProgrammableStageDescriptor = @import("main.zig").ProgrammableStageDescriptor;
|
||||
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
|
||||
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const ComputePipeline = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
layout: ?*PipelineLayout = null,
|
||||
compute: ProgrammableStageDescriptor,
|
||||
};
|
||||
|
||||
pub inline fn getBindGroupLayout(compute_pipeline: *ComputePipeline, group_index: u32) *BindGroupLayout {
|
||||
return Impl.computePipelineGetBindGroupLayout(compute_pipeline, group_index);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(compute_pipeline: *ComputePipeline, label: [*:0]const u8) void {
|
||||
Impl.computePipelineSetLabel(compute_pipeline, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(compute_pipeline: *ComputePipeline) void {
|
||||
Impl.computePipelineReference(compute_pipeline);
|
||||
}
|
||||
|
||||
pub inline fn release(compute_pipeline: *ComputePipeline) void {
|
||||
Impl.computePipelineRelease(compute_pipeline);
|
||||
}
|
||||
};
|
||||
74
src/sysgpu/sysgpu/dawn.zig
Normal file
74
src/sysgpu/sysgpu/dawn.zig
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ChainedStructOut = @import("main.zig").ChainedStructOut;
|
||||
const PowerPreference = @import("main.zig").PowerPreference;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
|
||||
pub const CacheDeviceDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_cache_device_descriptor },
|
||||
isolation_key: [*:0]const u8 = "",
|
||||
};
|
||||
|
||||
pub const EncoderInternalUsageDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_encoder_internal_usage_descriptor },
|
||||
use_internal_usages: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub const MultisampleStateRenderToSingleSampled = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_multisample_state_render_to_single_sampled },
|
||||
enabled: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub const RenderPassColorAttachmentRenderToSingleSampled = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_render_pass_color_attachment_render_to_single_sampled },
|
||||
implicit_sample_count: u32 = 1,
|
||||
};
|
||||
|
||||
pub const TextureInternalUsageDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_texture_internal_usage_descriptor },
|
||||
internal_usage: Texture.UsageFlags = Texture.UsageFlags.none,
|
||||
};
|
||||
|
||||
pub const TogglesDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor },
|
||||
enabled_toggles_count: usize = 0,
|
||||
enabled_toggles: ?[*]const [*:0]const u8 = null,
|
||||
disabled_toggles_count: usize = 0,
|
||||
disabled_toggles: ?[*]const [*:0]const u8 = null,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor },
|
||||
enabled_toggles: ?[]const [*:0]const u8 = null,
|
||||
disabled_toggles: ?[]const [*:0]const u8 = null,
|
||||
}) TogglesDescriptor {
|
||||
return .{
|
||||
.chain = v.chain,
|
||||
.enabled_toggles_count = if (v.enabled_toggles) |e| e.len else 0,
|
||||
.enabled_toggles = if (v.enabled_toggles) |e| e.ptr else null,
|
||||
.disabled_toggles_count = if (v.disabled_toggles) |e| e.len else 0,
|
||||
.disabled_toggles = if (v.disabled_toggles) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ShaderModuleSPIRVOptionsDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_shader_module_spirv_options_descriptor },
|
||||
allow_non_uniform_derivatives: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub const AdapterPropertiesPowerPreference = extern struct {
|
||||
chain: ChainedStructOut = .{
|
||||
.next = null,
|
||||
.s_type = .dawn_adapter_properties_power_preference,
|
||||
},
|
||||
power_preference: PowerPreference = .undefined,
|
||||
};
|
||||
|
||||
pub const BufferDescriptorErrorInfoFromWireClient = extern struct {
|
||||
chain: ChainedStruct = .{
|
||||
.next = null,
|
||||
.s_type = .dawn_buffer_descriptor_error_info_from_wire_client,
|
||||
},
|
||||
out_of_memory: Bool32 = .false,
|
||||
};
|
||||
412
src/sysgpu/sysgpu/device.zig
Normal file
412
src/sysgpu/sysgpu/device.zig
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
const std = @import("std");
|
||||
const Adapter = @import("adapter.zig").Adapter;
|
||||
const Queue = @import("queue.zig").Queue;
|
||||
const BindGroup = @import("bind_group.zig").BindGroup;
|
||||
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const CommandEncoder = @import("command_encoder.zig").CommandEncoder;
|
||||
const ComputePipeline = @import("compute_pipeline.zig").ComputePipeline;
|
||||
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
|
||||
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
|
||||
const QuerySet = @import("query_set.zig").QuerySet;
|
||||
const RenderBundleEncoder = @import("render_bundle_encoder.zig").RenderBundleEncoder;
|
||||
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
|
||||
const Sampler = @import("sampler.zig").Sampler;
|
||||
const ShaderModule = @import("shader_module.zig").ShaderModule;
|
||||
const Surface = @import("surface.zig").Surface;
|
||||
const SwapChain = @import("swap_chain.zig").SwapChain;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const FeatureName = @import("main.zig").FeatureName;
|
||||
const RequiredLimits = @import("main.zig").RequiredLimits;
|
||||
const SupportedLimits = @import("main.zig").SupportedLimits;
|
||||
const ErrorType = @import("main.zig").ErrorType;
|
||||
const ErrorFilter = @import("main.zig").ErrorFilter;
|
||||
const LoggingType = @import("main.zig").LoggingType;
|
||||
const CreatePipelineAsyncStatus = @import("main.zig").CreatePipelineAsyncStatus;
|
||||
const LoggingCallback = @import("main.zig").LoggingCallback;
|
||||
const ErrorCallback = @import("main.zig").ErrorCallback;
|
||||
const CreateComputePipelineAsyncCallback = @import("main.zig").CreateComputePipelineAsyncCallback;
|
||||
const CreateRenderPipelineAsyncCallback = @import("main.zig").CreateRenderPipelineAsyncCallback;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const dawn = @import("dawn.zig");
|
||||
|
||||
pub const Device = opaque {
|
||||
pub const LostCallback = *const fn (
|
||||
reason: LostReason,
|
||||
message: [*:0]const u8,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void;
|
||||
|
||||
pub const LostReason = enum(u32) {
|
||||
undefined = 0x00000000,
|
||||
destroyed = 0x00000001,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
dawn_toggles_descriptor: *const dawn.TogglesDescriptor,
|
||||
dawn_cache_device_descriptor: *const dawn.CacheDeviceDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
required_features_count: usize = 0,
|
||||
required_features: ?[*]const FeatureName = null,
|
||||
required_limits: ?*const RequiredLimits = null,
|
||||
default_queue: Queue.Descriptor = Queue.Descriptor{},
|
||||
device_lost_callback: LostCallback,
|
||||
device_lost_userdata: ?*anyopaque,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
required_features: ?[]const FeatureName = null,
|
||||
required_limits: ?*const RequiredLimits = null,
|
||||
default_queue: Queue.Descriptor = Queue.Descriptor{},
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.required_features_count = if (v.required_features) |e| e.len else 0,
|
||||
.required_features = if (v.required_features) |e| e.ptr else null,
|
||||
.default_queue = v.default_queue,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn createBindGroup(device: *Device, descriptor: *const BindGroup.Descriptor) *BindGroup {
|
||||
return Impl.deviceCreateBindGroup(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createBindGroupLayout(device: *Device, descriptor: *const BindGroupLayout.Descriptor) *BindGroupLayout {
|
||||
return Impl.deviceCreateBindGroupLayout(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createBuffer(device: *Device, descriptor: *const Buffer.Descriptor) *Buffer {
|
||||
return Impl.deviceCreateBuffer(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createCommandEncoder(device: *Device, descriptor: ?*const CommandEncoder.Descriptor) *CommandEncoder {
|
||||
return Impl.deviceCreateCommandEncoder(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createComputePipeline(device: *Device, descriptor: *const ComputePipeline.Descriptor) *ComputePipeline {
|
||||
return Impl.deviceCreateComputePipeline(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createComputePipelineAsync(
|
||||
device: *Device,
|
||||
descriptor: *const ComputePipeline.Descriptor,
|
||||
context: anytype,
|
||||
comptime callback: fn (
|
||||
status: CreatePipelineAsyncStatus,
|
||||
compute_pipeline: *ComputePipeline,
|
||||
message: [*:0]const u8,
|
||||
ctx: @TypeOf(context),
|
||||
) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(
|
||||
status: CreatePipelineAsyncStatus,
|
||||
compute_pipeline: *ComputePipeline,
|
||||
message: [*:0]const u8,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
callback(
|
||||
status,
|
||||
compute_pipeline,
|
||||
message,
|
||||
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
|
||||
);
|
||||
}
|
||||
};
|
||||
Impl.deviceCreateComputePipelineAsync(device, descriptor, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn createErrorBuffer(device: *Device, descriptor: *const Buffer.Descriptor) *Buffer {
|
||||
return Impl.deviceCreateErrorBuffer(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createErrorExternalTexture(device: *Device) *ExternalTexture {
|
||||
return Impl.deviceCreateErrorExternalTexture(device);
|
||||
}
|
||||
|
||||
pub inline fn createErrorTexture(device: *Device, descriptor: *const Texture.Descriptor) *Texture {
|
||||
return Impl.deviceCreateErrorTexture(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createExternalTexture(device: *Device, external_texture_descriptor: *const ExternalTexture.Descriptor) *ExternalTexture {
|
||||
return Impl.deviceCreateExternalTexture(device, external_texture_descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createPipelineLayout(device: *Device, pipeline_layout_descriptor: *const PipelineLayout.Descriptor) *PipelineLayout {
|
||||
return Impl.deviceCreatePipelineLayout(device, pipeline_layout_descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createQuerySet(device: *Device, descriptor: *const QuerySet.Descriptor) *QuerySet {
|
||||
return Impl.deviceCreateQuerySet(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createRenderBundleEncoder(device: *Device, descriptor: *const RenderBundleEncoder.Descriptor) *RenderBundleEncoder {
|
||||
return Impl.deviceCreateRenderBundleEncoder(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createRenderPipeline(device: *Device, descriptor: *const RenderPipeline.Descriptor) *RenderPipeline {
|
||||
return Impl.deviceCreateRenderPipeline(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createRenderPipelineAsync(
|
||||
device: *Device,
|
||||
descriptor: *const RenderPipeline.Descriptor,
|
||||
context: anytype,
|
||||
comptime callback: fn (
|
||||
ctx: @TypeOf(context),
|
||||
status: CreatePipelineAsyncStatus,
|
||||
pipeline: *RenderPipeline,
|
||||
message: [*:0]const u8,
|
||||
) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(
|
||||
status: CreatePipelineAsyncStatus,
|
||||
pipeline: *RenderPipeline,
|
||||
message: [*:0]const u8,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
callback(
|
||||
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
|
||||
status,
|
||||
pipeline,
|
||||
message,
|
||||
);
|
||||
}
|
||||
};
|
||||
Impl.deviceCreateRenderPipelineAsync(device, descriptor, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn createSampler(device: *Device, descriptor: ?*const Sampler.Descriptor) *Sampler {
|
||||
return Impl.deviceCreateSampler(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createShaderModule(device: *Device, descriptor: *const ShaderModule.Descriptor) *ShaderModule {
|
||||
return Impl.deviceCreateShaderModule(device, descriptor);
|
||||
}
|
||||
|
||||
/// Helper to make createShaderModule invocations slightly nicer.
|
||||
pub inline fn createShaderModuleWGSL(
|
||||
device: *Device,
|
||||
label: ?[*:0]const u8,
|
||||
code: [*:0]const u8,
|
||||
) *ShaderModule {
|
||||
return device.createShaderModule(&ShaderModule.Descriptor{
|
||||
.next_in_chain = .{ .wgsl_descriptor = &.{ .code = code } },
|
||||
.label = label,
|
||||
});
|
||||
}
|
||||
|
||||
/// Helper to make createShaderModule invocations slightly nicer.
|
||||
pub inline fn createShaderModuleSpirV(
|
||||
device: *Device,
|
||||
label: ?[*:0]const u8,
|
||||
code: [*]const u32,
|
||||
code_size: u32,
|
||||
) *ShaderModule {
|
||||
return device.createShaderModule(&ShaderModule.Descriptor{
|
||||
.next_in_chain = .{ .spirv_descriptor = &.{
|
||||
.code_size = code_size,
|
||||
.code = code,
|
||||
} },
|
||||
.label = label,
|
||||
});
|
||||
}
|
||||
|
||||
/// Helper to make createShaderModule invocations slightly nicer.
|
||||
pub inline fn createShaderModuleHLSL(
|
||||
device: *Device,
|
||||
label: ?[*:0]const u8,
|
||||
code: []const u8,
|
||||
) *ShaderModule {
|
||||
return device.createShaderModule(&ShaderModule.Descriptor{
|
||||
.next_in_chain = .{ .hlsl_descriptor = &.{
|
||||
.code = code.ptr,
|
||||
.code_size = code.len,
|
||||
} },
|
||||
.label = label,
|
||||
});
|
||||
}
|
||||
|
||||
/// Helper to make createShaderModule invocations slightly nicer.
|
||||
pub inline fn createShaderModuleMSL(
|
||||
device: *Device,
|
||||
label: ?[*:0]const u8,
|
||||
code: []const u8,
|
||||
) *ShaderModule {
|
||||
return device.createShaderModule(&ShaderModule.Descriptor{
|
||||
.next_in_chain = .{ .msl_descriptor = &.{
|
||||
.code = code.ptr,
|
||||
.code_size = code.len,
|
||||
} },
|
||||
.label = label,
|
||||
});
|
||||
}
|
||||
|
||||
pub inline fn createSwapChain(device: *Device, surface: ?*Surface, descriptor: *const SwapChain.Descriptor) *SwapChain {
|
||||
return Impl.deviceCreateSwapChain(device, surface, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn createTexture(device: *Device, descriptor: *const Texture.Descriptor) *Texture {
|
||||
return Impl.deviceCreateTexture(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn destroy(device: *Device) void {
|
||||
Impl.deviceDestroy(device);
|
||||
}
|
||||
|
||||
/// Call once with null to determine the array length, and again to fetch the feature list.
|
||||
///
|
||||
/// Consider using the enumerateFeaturesOwned helper.
|
||||
pub inline fn enumerateFeatures(device: *Device, features: ?[*]FeatureName) usize {
|
||||
return Impl.deviceEnumerateFeatures(device, features);
|
||||
}
|
||||
|
||||
/// Enumerates the adapter features, storing the result in an allocated slice which is owned by
|
||||
/// the caller.
|
||||
pub inline fn enumerateFeaturesOwned(device: *Device, allocator: std.mem.Allocator) ![]FeatureName {
|
||||
const count = device.enumerateFeatures(null);
|
||||
const data = try allocator.alloc(FeatureName, count);
|
||||
_ = device.enumerateFeatures(data.ptr);
|
||||
return data;
|
||||
}
|
||||
|
||||
pub inline fn forceLoss(device: *Device, reason: LostReason, message: [*:0]const u8) void {
|
||||
return Impl.deviceForceLoss(device, reason, message);
|
||||
}
|
||||
|
||||
pub inline fn getAdapter(device: *Device) *Adapter {
|
||||
return Impl.deviceGetAdapter(device);
|
||||
}
|
||||
|
||||
pub inline fn getLimits(device: *Device, limits: *SupportedLimits) bool {
|
||||
return Impl.deviceGetLimits(device, limits);
|
||||
}
|
||||
|
||||
pub inline fn getQueue(device: *Device) *Queue {
|
||||
return Impl.deviceGetQueue(device);
|
||||
}
|
||||
|
||||
pub inline fn hasFeature(device: *Device, feature: FeatureName) bool {
|
||||
return Impl.deviceHasFeature(device, feature);
|
||||
}
|
||||
|
||||
pub inline fn injectError(device: *Device, typ: ErrorType, message: [*:0]const u8) void {
|
||||
Impl.deviceInjectError(device, typ, message);
|
||||
}
|
||||
|
||||
pub inline fn popErrorScope(
|
||||
device: *Device,
|
||||
context: anytype,
|
||||
comptime callback: fn (ctx: @TypeOf(context), typ: ErrorType, message: [*:0]const u8) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(typ: ErrorType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
|
||||
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
|
||||
}
|
||||
};
|
||||
Impl.devicePopErrorScope(device, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn pushErrorScope(device: *Device, filter: ErrorFilter) void {
|
||||
Impl.devicePushErrorScope(device, filter);
|
||||
}
|
||||
|
||||
pub inline fn setDeviceLostCallback(
|
||||
device: *Device,
|
||||
context: anytype,
|
||||
comptime callback: ?fn (ctx: @TypeOf(context), reason: LostReason, message: [*:0]const u8) callconv(.Inline) void,
|
||||
) void {
|
||||
if (callback) |cb| {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(reason: LostReason, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
|
||||
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), reason, message);
|
||||
}
|
||||
};
|
||||
Impl.deviceSetDeviceLostCallback(device, Helper.cCallback, if (Context == void) null else context);
|
||||
} else {
|
||||
Impl.deviceSetDeviceLostCallback(device, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn setLabel(device: *Device, label: [*:0]const u8) void {
|
||||
Impl.deviceSetLabel(device, label);
|
||||
}
|
||||
|
||||
pub inline fn setLoggingCallback(
|
||||
device: *Device,
|
||||
context: anytype,
|
||||
comptime callback: ?fn (ctx: @TypeOf(context), typ: LoggingType, message: [*:0]const u8) callconv(.Inline) void,
|
||||
) void {
|
||||
if (callback) |cb| {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(typ: LoggingType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
|
||||
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
|
||||
}
|
||||
};
|
||||
Impl.deviceSetLoggingCallback(device, Helper.cCallback, if (Context == void) null else context);
|
||||
} else {
|
||||
Impl.deviceSetLoggingCallback(device, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn setUncapturedErrorCallback(
|
||||
device: *Device,
|
||||
context: anytype,
|
||||
comptime callback: ?fn (ctx: @TypeOf(context), typ: ErrorType, message: [*:0]const u8) callconv(.Inline) void,
|
||||
) void {
|
||||
if (callback) |cb| {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(typ: ErrorType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
|
||||
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
|
||||
}
|
||||
};
|
||||
Impl.deviceSetUncapturedErrorCallback(device, Helper.cCallback, if (Context == void) null else context);
|
||||
} else {
|
||||
Impl.deviceSetUncapturedErrorCallback(device, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn tick(device: *Device) void {
|
||||
Impl.deviceTick(device);
|
||||
}
|
||||
|
||||
// Mach WebGPU extension. Supported with mach-gpu-dawn.
|
||||
//
|
||||
// When making Metal interop with other APIs, we need to be careful that QueueSubmit doesn't
|
||||
// mean that the operations will be visible to other APIs/Metal devices right away. macOS
|
||||
// does have a global queue of graphics operations, but the command buffers are inserted there
|
||||
// when they are "scheduled". Submitting other operations before the command buffer is
|
||||
// scheduled could lead to races in who gets scheduled first and incorrect rendering.
|
||||
pub inline fn machWaitForCommandsToBeScheduled(device: *Device) void {
|
||||
Impl.machDeviceWaitForCommandsToBeScheduled(device);
|
||||
}
|
||||
|
||||
pub inline fn validateTextureDescriptor(device: *Device, descriptor: *const Texture.Descriptor) void {
|
||||
Impl.deviceVlidateTextureDescriptor(device, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn reference(device: *Device) void {
|
||||
Impl.deviceReference(device);
|
||||
}
|
||||
|
||||
pub inline fn release(device: *Device) void {
|
||||
Impl.deviceRelease(device);
|
||||
}
|
||||
};
|
||||
56
src/sysgpu/sysgpu/external_texture.zig
Normal file
56
src/sysgpu/sysgpu/external_texture.zig
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const TextureView = @import("texture_view.zig").TextureView;
|
||||
const Origin2D = @import("main.zig").Origin2D;
|
||||
const Extent2D = @import("main.zig").Extent2D;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const ExternalTexture = opaque {
|
||||
pub const BindingEntry = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_entry },
|
||||
external_texture: *ExternalTexture,
|
||||
};
|
||||
|
||||
pub const BindingLayout = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_layout },
|
||||
};
|
||||
|
||||
const Rotation = enum(u32) {
|
||||
rotate_0_degrees = 0x00000000,
|
||||
rotate_90_degrees = 0x00000001,
|
||||
rotate_180_degrees = 0x00000002,
|
||||
rotate_270_degrees = 0x00000003,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
plane0: *TextureView,
|
||||
plane1: ?*TextureView = null,
|
||||
visible_origin: Origin2D,
|
||||
visible_size: Extent2D,
|
||||
do_yuv_to_rgb_conversion_only: Bool32 = .false,
|
||||
yuv_to_rgb_conversion_matrix: ?*const [12]f32 = null,
|
||||
src_transform_function_parameters: *const [7]f32,
|
||||
dst_transform_function_parameters: *const [7]f32,
|
||||
gamut_conversion_matrix: *const [9]f32,
|
||||
flip_y: Bool32,
|
||||
rotation: Rotation,
|
||||
};
|
||||
|
||||
pub inline fn destroy(external_texture: *ExternalTexture) void {
|
||||
Impl.externalTextureDestroy(external_texture);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(external_texture: *ExternalTexture, label: [*:0]const u8) void {
|
||||
Impl.externalTextureSetLabel(external_texture, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(external_texture: *ExternalTexture) void {
|
||||
Impl.externalTextureReference(external_texture);
|
||||
}
|
||||
|
||||
pub inline fn release(external_texture: *ExternalTexture) void {
|
||||
Impl.externalTextureRelease(external_texture);
|
||||
}
|
||||
};
|
||||
65
src/sysgpu/sysgpu/instance.zig
Normal file
65
src/sysgpu/sysgpu/instance.zig
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const RequestAdapterStatus = @import("main.zig").RequestAdapterStatus;
|
||||
const Surface = @import("surface.zig").Surface;
|
||||
const Adapter = @import("adapter.zig").Adapter;
|
||||
const RequestAdapterOptions = @import("main.zig").RequestAdapterOptions;
|
||||
const RequestAdapterCallback = @import("main.zig").RequestAdapterCallback;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const dawn = @import("dawn.zig");
|
||||
|
||||
pub const Instance = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
dawn_toggles_descriptor: *const dawn.TogglesDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
};
|
||||
|
||||
pub inline fn createSurface(instance: *Instance, descriptor: *const Surface.Descriptor) *Surface {
|
||||
return Impl.instanceCreateSurface(instance, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn processEvents(instance: *Instance) void {
|
||||
Impl.instanceProcessEvents(instance);
|
||||
}
|
||||
|
||||
pub inline fn requestAdapter(
|
||||
instance: *Instance,
|
||||
options: ?*const RequestAdapterOptions,
|
||||
context: anytype,
|
||||
comptime callback: fn (
|
||||
ctx: @TypeOf(context),
|
||||
status: RequestAdapterStatus,
|
||||
adapter: ?*Adapter,
|
||||
message: ?[*:0]const u8,
|
||||
) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(
|
||||
status: RequestAdapterStatus,
|
||||
adapter: ?*Adapter,
|
||||
message: ?[*:0]const u8,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
callback(
|
||||
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
|
||||
status,
|
||||
adapter,
|
||||
message,
|
||||
);
|
||||
}
|
||||
};
|
||||
Impl.instanceRequestAdapter(instance, options, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn reference(instance: *Instance) void {
|
||||
Impl.instanceReference(instance);
|
||||
}
|
||||
|
||||
pub inline fn release(instance: *Instance) void {
|
||||
Impl.instanceRelease(instance);
|
||||
}
|
||||
};
|
||||
2697
src/sysgpu/sysgpu/interface.zig
Normal file
2697
src/sysgpu/sysgpu/interface.zig
Normal file
File diff suppressed because it is too large
Load diff
1027
src/sysgpu/sysgpu/main.zig
Normal file
1027
src/sysgpu/sysgpu/main.zig
Normal file
File diff suppressed because it is too large
Load diff
38
src/sysgpu/sysgpu/pipeline_layout.zig
Normal file
38
src/sysgpu/sysgpu/pipeline_layout.zig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const PipelineLayout = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
bind_group_layout_count: usize = 0,
|
||||
bind_group_layouts: ?[*]const *BindGroupLayout = null,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
bind_group_layouts: ?[]const *BindGroupLayout = null,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.bind_group_layout_count = if (v.bind_group_layouts) |e| e.len else 0,
|
||||
.bind_group_layouts = if (v.bind_group_layouts) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn setLabel(pipeline_layout: *PipelineLayout, label: [*:0]const u8) void {
|
||||
Impl.pipelineLayoutSetLabel(pipeline_layout, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(pipeline_layout: *PipelineLayout) void {
|
||||
Impl.pipelineLayoutReference(pipeline_layout);
|
||||
}
|
||||
|
||||
pub inline fn release(pipeline_layout: *PipelineLayout) void {
|
||||
Impl.pipelineLayoutRelease(pipeline_layout);
|
||||
}
|
||||
};
|
||||
57
src/sysgpu/sysgpu/query_set.zig
Normal file
57
src/sysgpu/sysgpu/query_set.zig
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const PipelineStatisticName = @import("main.zig").PipelineStatisticName;
|
||||
const QueryType = @import("main.zig").QueryType;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const QuerySet = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
type: QueryType,
|
||||
count: u32,
|
||||
pipeline_statistics: ?[*]const PipelineStatisticName = null,
|
||||
pipeline_statistics_count: usize = 0,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
type: QueryType,
|
||||
count: u32,
|
||||
pipeline_statistics: ?[]const PipelineStatisticName = null,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.type = v.type,
|
||||
.count = v.count,
|
||||
.pipeline_statistics_count = if (v.pipeline_statistics) |e| e.len else 0,
|
||||
.pipeline_statistics = if (v.pipeline_statistics) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn destroy(query_set: *QuerySet) void {
|
||||
Impl.querySetDestroy(query_set);
|
||||
}
|
||||
|
||||
pub inline fn getCount(query_set: *QuerySet) u32 {
|
||||
return Impl.querySetGetCount(query_set);
|
||||
}
|
||||
|
||||
pub inline fn getType(query_set: *QuerySet) QueryType {
|
||||
return Impl.querySetGetType(query_set);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(query_set: *QuerySet, label: [*:0]const u8) void {
|
||||
Impl.querySetSetLabel(query_set, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(query_set: *QuerySet) void {
|
||||
Impl.querySetReference(query_set);
|
||||
}
|
||||
|
||||
pub inline fn release(query_set: *QuerySet) void {
|
||||
Impl.querySetRelease(query_set);
|
||||
}
|
||||
};
|
||||
101
src/sysgpu/sysgpu/queue.zig
Normal file
101
src/sysgpu/sysgpu/queue.zig
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
const std = @import("std");
|
||||
const CommandBuffer = @import("command_buffer.zig").CommandBuffer;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
const ImageCopyTexture = @import("main.zig").ImageCopyTexture;
|
||||
const ImageCopyExternalTexture = @import("main.zig").ImageCopyExternalTexture;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const Extent3D = @import("main.zig").Extent3D;
|
||||
const CopyTextureForBrowserOptions = @import("main.zig").CopyTextureForBrowserOptions;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const Queue = opaque {
|
||||
pub const WorkDoneCallback = *const fn (
|
||||
status: WorkDoneStatus,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void;
|
||||
|
||||
pub const WorkDoneStatus = enum(u32) {
|
||||
success = 0x00000000,
|
||||
err = 0x00000001,
|
||||
unknown = 0x00000002,
|
||||
device_lost = 0x00000003,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub inline fn copyExternalTextureForBrowser(queue: *Queue, source: *const ImageCopyExternalTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void {
|
||||
Impl.queueCopyExternalTextureForBrowser(queue, source, destination, copy_size, options);
|
||||
}
|
||||
|
||||
pub inline fn copyTextureForBrowser(queue: *Queue, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void {
|
||||
Impl.queueCopyTextureForBrowser(queue, source, destination, copy_size, options);
|
||||
}
|
||||
|
||||
// TODO: dawn: does not allow unsetting this callback to null
|
||||
pub inline fn onSubmittedWorkDone(
|
||||
queue: *Queue,
|
||||
signal_value: u64,
|
||||
context: anytype,
|
||||
comptime callback: fn (ctx: @TypeOf(context), status: WorkDoneStatus) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(status: WorkDoneStatus, userdata: ?*anyopaque) callconv(.C) void {
|
||||
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status);
|
||||
}
|
||||
};
|
||||
Impl.queueOnSubmittedWorkDone(queue, signal_value, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(queue: *Queue, label: [*:0]const u8) void {
|
||||
Impl.queueSetLabel(queue, label);
|
||||
}
|
||||
|
||||
pub inline fn submit(queue: *Queue, commands: []const *const CommandBuffer) void {
|
||||
Impl.queueSubmit(queue, commands.len, commands.ptr);
|
||||
}
|
||||
|
||||
pub inline fn writeBuffer(
|
||||
queue: *Queue,
|
||||
buffer: *Buffer,
|
||||
buffer_offset_bytes: u64,
|
||||
data_slice: anytype,
|
||||
) void {
|
||||
Impl.queueWriteBuffer(
|
||||
queue,
|
||||
buffer,
|
||||
buffer_offset_bytes,
|
||||
@as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
|
||||
data_slice.len * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn writeTexture(
|
||||
queue: *Queue,
|
||||
destination: *const ImageCopyTexture,
|
||||
data_layout: *const Texture.DataLayout,
|
||||
write_size: *const Extent3D,
|
||||
data_slice: anytype,
|
||||
) void {
|
||||
Impl.queueWriteTexture(
|
||||
queue,
|
||||
destination,
|
||||
@as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
|
||||
@as(usize, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
|
||||
data_layout,
|
||||
write_size,
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn reference(queue: *Queue) void {
|
||||
Impl.queueReference(queue);
|
||||
}
|
||||
|
||||
pub inline fn release(queue: *Queue) void {
|
||||
Impl.queueRelease(queue);
|
||||
}
|
||||
};
|
||||
21
src/sysgpu/sysgpu/render_bundle.zig
Normal file
21
src/sysgpu/sysgpu/render_bundle.zig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const RenderBundle = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub inline fn setLabel(render_bundle: *RenderBundle, label: [*:0]const u8) void {
|
||||
Impl.renderBundleSetLabel(render_bundle, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(render_bundle: *RenderBundle) void {
|
||||
Impl.renderBundleReference(render_bundle);
|
||||
}
|
||||
|
||||
pub inline fn release(render_bundle: *RenderBundle) void {
|
||||
Impl.renderBundleRelease(render_bundle);
|
||||
}
|
||||
};
|
||||
122
src/sysgpu/sysgpu/render_bundle_encoder.zig
Normal file
122
src/sysgpu/sysgpu/render_bundle_encoder.zig
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
const Texture = @import("texture.zig").Texture;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const BindGroup = @import("bind_group.zig").BindGroup;
|
||||
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
|
||||
const RenderBundle = @import("render_bundle.zig").RenderBundle;
|
||||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const IndexFormat = @import("main.zig").IndexFormat;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const RenderBundleEncoder = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
color_formats_count: usize = 0,
|
||||
color_formats: ?[*]const Texture.Format = null,
|
||||
depth_stencil_format: Texture.Format = .undefined,
|
||||
sample_count: u32 = 1,
|
||||
depth_read_only: Bool32 = .false,
|
||||
stencil_read_only: Bool32 = .false,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
color_formats: ?[]const Texture.Format = null,
|
||||
depth_stencil_format: Texture.Format = .undefined,
|
||||
sample_count: u32 = 1,
|
||||
depth_read_only: bool = false,
|
||||
stencil_read_only: bool = false,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.color_formats_count = if (v.color_formats) |e| e.len else 0,
|
||||
.color_formats = if (v.color_formats) |e| e.ptr else null,
|
||||
.depth_stencil_format = v.depth_stencil_format,
|
||||
.sample_count = v.sample_count,
|
||||
.depth_read_only = Bool32.from(v.depth_read_only),
|
||||
.stencil_read_only = Bool32.from(v.stencil_read_only),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Default `instance_count`: 1
|
||||
/// Default `first_vertex`: 0
|
||||
/// Default `first_instance`: 0
|
||||
pub inline fn draw(render_bundle_encoder: *RenderBundleEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
|
||||
Impl.renderBundleEncoderDraw(render_bundle_encoder, vertex_count, instance_count, first_vertex, first_instance);
|
||||
}
|
||||
|
||||
/// Default `instance_count`: 1
|
||||
/// Default `first_index`: 0
|
||||
/// Default `base_vertex`: 0
|
||||
/// Default `first_instance`: 0
|
||||
pub inline fn drawIndexed(render_bundle_encoder: *RenderBundleEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void {
|
||||
Impl.renderBundleEncoderDrawIndexed(render_bundle_encoder, index_count, instance_count, first_index, base_vertex, first_instance);
|
||||
}
|
||||
|
||||
pub inline fn drawIndexedIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
|
||||
Impl.renderBundleEncoderDrawIndexedIndirect(render_bundle_encoder, indirect_buffer, indirect_offset);
|
||||
}
|
||||
|
||||
pub inline fn drawIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
|
||||
Impl.renderBundleEncoderDrawIndirect(render_bundle_encoder, indirect_buffer, indirect_offset);
|
||||
}
|
||||
|
||||
pub inline fn finish(render_bundle_encoder: *RenderBundleEncoder, descriptor: ?*const RenderBundle.Descriptor) *RenderBundle {
|
||||
return Impl.renderBundleEncoderFinish(render_bundle_encoder, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn insertDebugMarker(render_bundle_encoder: *RenderBundleEncoder, marker_label: [*:0]const u8) void {
|
||||
Impl.renderBundleEncoderInsertDebugMarker(render_bundle_encoder, marker_label);
|
||||
}
|
||||
|
||||
pub inline fn popDebugGroup(render_bundle_encoder: *RenderBundleEncoder) void {
|
||||
Impl.renderBundleEncoderPopDebugGroup(render_bundle_encoder);
|
||||
}
|
||||
|
||||
pub inline fn pushDebugGroup(render_bundle_encoder: *RenderBundleEncoder, group_label: [*:0]const u8) void {
|
||||
Impl.renderBundleEncoderPushDebugGroup(render_bundle_encoder, group_label);
|
||||
}
|
||||
|
||||
/// Default `dynamic_offsets`: `null`
|
||||
pub inline fn setBindGroup(render_bundle_encoder: *RenderBundleEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
|
||||
Impl.renderBundleEncoderSetBindGroup(
|
||||
render_bundle_encoder,
|
||||
group_index,
|
||||
group,
|
||||
if (dynamic_offsets) |v| v.len else 0,
|
||||
if (dynamic_offsets) |v| v.ptr else null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Default `offset`: 0
|
||||
/// Default `size`: `gpu.whole_size`
|
||||
pub inline fn setIndexBuffer(render_bundle_encoder: *RenderBundleEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void {
|
||||
Impl.renderBundleEncoderSetIndexBuffer(render_bundle_encoder, buffer, format, offset, size);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(render_bundle_encoder: *RenderBundleEncoder, label: [*:0]const u8) void {
|
||||
Impl.renderBundleEncoderSetLabel(render_bundle_encoder, label);
|
||||
}
|
||||
|
||||
pub inline fn setPipeline(render_bundle_encoder: *RenderBundleEncoder, pipeline: *RenderPipeline) void {
|
||||
Impl.renderBundleEncoderSetPipeline(render_bundle_encoder, pipeline);
|
||||
}
|
||||
|
||||
/// Default `offset`: 0
|
||||
/// Default `size`: `gpu.whole_size`
|
||||
pub inline fn setVertexBuffer(render_bundle_encoder: *RenderBundleEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void {
|
||||
Impl.renderBundleEncoderSetVertexBuffer(render_bundle_encoder, slot, buffer, offset, size);
|
||||
}
|
||||
|
||||
pub inline fn reference(render_bundle_encoder: *RenderBundleEncoder) void {
|
||||
Impl.renderBundleEncoderReference(render_bundle_encoder);
|
||||
}
|
||||
|
||||
pub inline fn release(render_bundle_encoder: *RenderBundleEncoder) void {
|
||||
Impl.renderBundleEncoderRelease(render_bundle_encoder);
|
||||
}
|
||||
};
|
||||
128
src/sysgpu/sysgpu/render_pass_encoder.zig
Normal file
128
src/sysgpu/sysgpu/render_pass_encoder.zig
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const RenderBundle = @import("render_bundle.zig").RenderBundle;
|
||||
const BindGroup = @import("bind_group.zig").BindGroup;
|
||||
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
|
||||
const QuerySet = @import("query_set.zig").QuerySet;
|
||||
const Color = @import("main.zig").Color;
|
||||
const IndexFormat = @import("main.zig").IndexFormat;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const RenderPassEncoder = opaque {
|
||||
pub inline fn beginOcclusionQuery(render_pass_encoder: *RenderPassEncoder, query_index: u32) void {
|
||||
Impl.renderPassEncoderBeginOcclusionQuery(render_pass_encoder, query_index);
|
||||
}
|
||||
|
||||
/// Default `instance_count`: 1
|
||||
/// Default `first_vertex`: 0
|
||||
/// Default `first_instance`: 0
|
||||
pub inline fn draw(render_pass_encoder: *RenderPassEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
|
||||
Impl.renderPassEncoderDraw(render_pass_encoder, vertex_count, instance_count, first_vertex, first_instance);
|
||||
}
|
||||
|
||||
/// Default `instance_count`: 1
|
||||
/// Default `first_index`: 0
|
||||
/// Default `base_vertex`: 0
|
||||
/// Default `first_instance`: 0
|
||||
pub inline fn drawIndexed(render_pass_encoder: *RenderPassEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void {
|
||||
Impl.renderPassEncoderDrawIndexed(render_pass_encoder, index_count, instance_count, first_index, base_vertex, first_instance);
|
||||
}
|
||||
|
||||
pub inline fn drawIndexedIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
|
||||
Impl.renderPassEncoderDrawIndexedIndirect(render_pass_encoder, indirect_buffer, indirect_offset);
|
||||
}
|
||||
|
||||
pub inline fn drawIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
|
||||
Impl.renderPassEncoderDrawIndirect(render_pass_encoder, indirect_buffer, indirect_offset);
|
||||
}
|
||||
|
||||
pub inline fn end(render_pass_encoder: *RenderPassEncoder) void {
|
||||
Impl.renderPassEncoderEnd(render_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn endOcclusionQuery(render_pass_encoder: *RenderPassEncoder) void {
|
||||
Impl.renderPassEncoderEndOcclusionQuery(render_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn executeBundles(
|
||||
render_pass_encoder: *RenderPassEncoder,
|
||||
bundles: []*const RenderBundle,
|
||||
) void {
|
||||
Impl.renderPassEncoderExecuteBundles(
|
||||
render_pass_encoder,
|
||||
bundles.len,
|
||||
bundles.ptr,
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn insertDebugMarker(render_pass_encoder: *RenderPassEncoder, marker_label: [*:0]const u8) void {
|
||||
Impl.renderPassEncoderInsertDebugMarker(render_pass_encoder, marker_label);
|
||||
}
|
||||
|
||||
pub inline fn popDebugGroup(render_pass_encoder: *RenderPassEncoder) void {
|
||||
Impl.renderPassEncoderPopDebugGroup(render_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn pushDebugGroup(render_pass_encoder: *RenderPassEncoder, group_label: [*:0]const u8) void {
|
||||
Impl.renderPassEncoderPushDebugGroup(render_pass_encoder, group_label);
|
||||
}
|
||||
|
||||
/// Default `dynamic_offsets_count`: 0
|
||||
/// Default `dynamic_offsets`: `null`
|
||||
pub inline fn setBindGroup(render_pass_encoder: *RenderPassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
|
||||
Impl.renderPassEncoderSetBindGroup(
|
||||
render_pass_encoder,
|
||||
group_index,
|
||||
group,
|
||||
if (dynamic_offsets) |v| v.len else 0,
|
||||
if (dynamic_offsets) |v| v.ptr else null,
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn setBlendConstant(render_pass_encoder: *RenderPassEncoder, color: *const Color) void {
|
||||
Impl.renderPassEncoderSetBlendConstant(render_pass_encoder, color);
|
||||
}
|
||||
|
||||
/// Default `offset`: 0
|
||||
/// Default `size`: `gpu.whole_size`
|
||||
pub inline fn setIndexBuffer(render_pass_encoder: *RenderPassEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void {
|
||||
Impl.renderPassEncoderSetIndexBuffer(render_pass_encoder, buffer, format, offset, size);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(render_pass_encoder: *RenderPassEncoder, label: [*:0]const u8) void {
|
||||
Impl.renderPassEncoderSetLabel(render_pass_encoder, label);
|
||||
}
|
||||
|
||||
pub inline fn setPipeline(render_pass_encoder: *RenderPassEncoder, pipeline: *RenderPipeline) void {
|
||||
Impl.renderPassEncoderSetPipeline(render_pass_encoder, pipeline);
|
||||
}
|
||||
|
||||
pub inline fn setScissorRect(render_pass_encoder: *RenderPassEncoder, x: u32, y: u32, width: u32, height: u32) void {
|
||||
Impl.renderPassEncoderSetScissorRect(render_pass_encoder, x, y, width, height);
|
||||
}
|
||||
|
||||
pub inline fn setStencilReference(render_pass_encoder: *RenderPassEncoder, _reference: u32) void {
|
||||
Impl.renderPassEncoderSetStencilReference(render_pass_encoder, _reference);
|
||||
}
|
||||
|
||||
/// Default `offset`: 0
|
||||
/// Default `size`: `gpu.whole_size`
|
||||
pub inline fn setVertexBuffer(render_pass_encoder: *RenderPassEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void {
|
||||
Impl.renderPassEncoderSetVertexBuffer(render_pass_encoder, slot, buffer, offset, size);
|
||||
}
|
||||
|
||||
pub inline fn setViewport(render_pass_encoder: *RenderPassEncoder, x: f32, y: f32, width: f32, height: f32, min_depth: f32, max_depth: f32) void {
|
||||
Impl.renderPassEncoderSetViewport(render_pass_encoder, x, y, width, height, min_depth, max_depth);
|
||||
}
|
||||
|
||||
pub inline fn writeTimestamp(render_pass_encoder: *RenderPassEncoder, query_set: *QuerySet, query_index: u32) void {
|
||||
Impl.renderPassEncoderWriteTimestamp(render_pass_encoder, query_set, query_index);
|
||||
}
|
||||
|
||||
pub inline fn reference(render_pass_encoder: *RenderPassEncoder) void {
|
||||
Impl.renderPassEncoderReference(render_pass_encoder);
|
||||
}
|
||||
|
||||
pub inline fn release(render_pass_encoder: *RenderPassEncoder) void {
|
||||
Impl.renderPassEncoderRelease(render_pass_encoder);
|
||||
}
|
||||
};
|
||||
38
src/sysgpu/sysgpu/render_pipeline.zig
Normal file
38
src/sysgpu/sysgpu/render_pipeline.zig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const DepthStencilState = @import("main.zig").DepthStencilState;
|
||||
const MultisampleState = @import("main.zig").MultisampleState;
|
||||
const VertexState = @import("main.zig").VertexState;
|
||||
const PrimitiveState = @import("main.zig").PrimitiveState;
|
||||
const FragmentState = @import("main.zig").FragmentState;
|
||||
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
|
||||
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const RenderPipeline = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
layout: ?*PipelineLayout = null,
|
||||
vertex: VertexState,
|
||||
primitive: PrimitiveState = .{},
|
||||
depth_stencil: ?*const DepthStencilState = null,
|
||||
multisample: MultisampleState = .{},
|
||||
fragment: ?*const FragmentState = null,
|
||||
};
|
||||
|
||||
pub inline fn getBindGroupLayout(render_pipeline: *RenderPipeline, group_index: u32) *BindGroupLayout {
|
||||
return Impl.renderPipelineGetBindGroupLayout(render_pipeline, group_index);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(render_pipeline: *RenderPipeline, label: [*:0]const u8) void {
|
||||
Impl.renderPipelineSetLabel(render_pipeline, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(render_pipeline: *RenderPipeline) void {
|
||||
Impl.renderPipelineReference(render_pipeline);
|
||||
}
|
||||
|
||||
pub inline fn release(render_pipeline: *RenderPipeline) void {
|
||||
Impl.renderPipelineRelease(render_pipeline);
|
||||
}
|
||||
};
|
||||
52
src/sysgpu/sysgpu/sampler.zig
Normal file
52
src/sysgpu/sysgpu/sampler.zig
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const FilterMode = @import("main.zig").FilterMode;
|
||||
const MipmapFilterMode = @import("main.zig").MipmapFilterMode;
|
||||
const CompareFunction = @import("main.zig").CompareFunction;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const Sampler = opaque {
|
||||
pub const AddressMode = enum(u32) {
|
||||
repeat = 0x00000000,
|
||||
mirror_repeat = 0x00000001,
|
||||
clamp_to_edge = 0x00000002,
|
||||
};
|
||||
|
||||
pub const BindingType = enum(u32) {
|
||||
undefined = 0x00000000,
|
||||
filtering = 0x00000001,
|
||||
non_filtering = 0x00000002,
|
||||
comparison = 0x00000003,
|
||||
};
|
||||
|
||||
pub const BindingLayout = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
type: BindingType = .undefined,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
address_mode_u: AddressMode = .clamp_to_edge,
|
||||
address_mode_v: AddressMode = .clamp_to_edge,
|
||||
address_mode_w: AddressMode = .clamp_to_edge,
|
||||
mag_filter: FilterMode = .nearest,
|
||||
min_filter: FilterMode = .nearest,
|
||||
mipmap_filter: MipmapFilterMode = .nearest,
|
||||
lod_min_clamp: f32 = 0.0,
|
||||
lod_max_clamp: f32 = 32.0,
|
||||
compare: CompareFunction = .undefined,
|
||||
max_anisotropy: u16 = 1,
|
||||
};
|
||||
|
||||
pub inline fn setLabel(sampler: *Sampler, label: [*:0]const u8) void {
|
||||
Impl.samplerSetLabel(sampler, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(sampler: *Sampler) void {
|
||||
Impl.samplerReference(sampler);
|
||||
}
|
||||
|
||||
pub inline fn release(sampler: *Sampler) void {
|
||||
Impl.samplerRelease(sampler);
|
||||
}
|
||||
};
|
||||
86
src/sysgpu/sysgpu/shader_module.zig
Normal file
86
src/sysgpu/sysgpu/shader_module.zig
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const CompilationInfoCallback = @import("main.zig").CompilationInfoCallback;
|
||||
const CompilationInfoRequestStatus = @import("main.zig").CompilationInfoRequestStatus;
|
||||
const CompilationInfo = @import("main.zig").CompilationInfo;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const dawn = @import("dawn.zig");
|
||||
|
||||
pub const ShaderModule = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
spirv_descriptor: ?*const SPIRVDescriptor,
|
||||
wgsl_descriptor: ?*const WGSLDescriptor,
|
||||
hlsl_descriptor: ?*const HLSLDescriptor,
|
||||
msl_descriptor: ?*const MSLDescriptor,
|
||||
dawn_shader_module_spirv_options_descriptor: ?*const dawn.ShaderModuleSPIRVOptionsDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub const SPIRVDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_spirv_descriptor },
|
||||
code_size: u32,
|
||||
code: [*]const u32,
|
||||
};
|
||||
|
||||
pub const WGSLDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_wgsl_descriptor },
|
||||
code: [*:0]const u8,
|
||||
};
|
||||
|
||||
pub const HLSLDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_hlsl_descriptor },
|
||||
code: [*]const u8,
|
||||
code_size: u32,
|
||||
};
|
||||
|
||||
pub const MSLDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_msl_descriptor },
|
||||
code: [*]const u8,
|
||||
code_size: u32,
|
||||
workgroup_size: WorkgroupSize,
|
||||
};
|
||||
|
||||
pub const WorkgroupSize = extern struct { x: u32 = 1, y: u32 = 1, z: u32 = 1 };
|
||||
|
||||
pub inline fn getCompilationInfo(
|
||||
shader_module: *ShaderModule,
|
||||
context: anytype,
|
||||
comptime callback: fn (
|
||||
ctx: @TypeOf(context),
|
||||
status: CompilationInfoRequestStatus,
|
||||
compilation_info: *const CompilationInfo,
|
||||
) callconv(.Inline) void,
|
||||
) void {
|
||||
const Context = @TypeOf(context);
|
||||
const Helper = struct {
|
||||
pub fn cCallback(
|
||||
status: CompilationInfoRequestStatus,
|
||||
compilation_info: *const CompilationInfo,
|
||||
userdata: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
callback(
|
||||
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
|
||||
status,
|
||||
compilation_info,
|
||||
);
|
||||
}
|
||||
};
|
||||
Impl.shaderModuleGetCompilationInfo(shader_module, Helper.cCallback, if (Context == void) null else context);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(shader_module: *ShaderModule, label: [*:0]const u8) void {
|
||||
Impl.shaderModuleSetLabel(shader_module, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(shader_module: *ShaderModule) void {
|
||||
Impl.shaderModuleReference(shader_module);
|
||||
}
|
||||
|
||||
pub inline fn release(shader_module: *ShaderModule) void {
|
||||
Impl.shaderModuleRelease(shader_module);
|
||||
}
|
||||
};
|
||||
91
src/sysgpu/sysgpu/shared_fence.zig
Normal file
91
src/sysgpu/sysgpu/shared_fence.zig
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ChainedStructOut = @import("main.zig").ChainedStructOut;
|
||||
|
||||
pub const SharedFence = opaque {
|
||||
pub const Type = enum(u32) {
|
||||
shared_fence_type_undefined = 0x00000000,
|
||||
shared_fence_type_vk_semaphore_opaque_fd = 0x00000001,
|
||||
shared_fence_type_vk_semaphore_sync_fd = 0x00000002,
|
||||
shared_fence_type_vk_semaphore_zircon_handle = 0x00000003,
|
||||
shared_fence_type_dxgi_shared_handle = 0x00000004,
|
||||
shared_fence_type_mtl_shared_event = 0x00000005,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
vk_semaphore_opaque_fd_descriptor: *const VkSemaphoreOpaqueFDDescriptor,
|
||||
vk_semaphore_sync_fd_descriptor: *const VkSemaphoreSyncFDDescriptor,
|
||||
vk_semaphore_zircon_handle_descriptor: *const VkSemaphoreZirconHandleDescriptor,
|
||||
dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor,
|
||||
mtl_shared_event_descriptor: *const MTLSharedEventDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*]const u8,
|
||||
};
|
||||
|
||||
pub const DXGISharedHandleDescriptor = extern struct {
|
||||
chain: ChainedStruct,
|
||||
handle: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DXGISharedHandleExportInfo = extern struct {
|
||||
chain: ChainedStructOut,
|
||||
handle: *anyopaque,
|
||||
};
|
||||
|
||||
pub const ExportInfo = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStructOut,
|
||||
dxgi_shared_handle_export_info: *const DXGISharedHandleExportInfo,
|
||||
mtl_shared_event_export_info: *const MTLSharedEventExportInfo,
|
||||
vk_semaphore_opaque_fd_export_info: *const VkSemaphoreOpaqueFDExportInfo,
|
||||
vk_semaphore_sync_fd_export_info: *const VkSemaphoreSyncFDExportInfo,
|
||||
vk_semaphore_zircon_handle_export_info: *const VkSemaphoreZirconHandleExportInfo,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
type: Type,
|
||||
};
|
||||
|
||||
pub const MTLSharedEventDescriptor = extern struct {
|
||||
chain: ChainedStruct,
|
||||
shared_event: *anyopaque,
|
||||
};
|
||||
|
||||
pub const MTLSharedEventExportInfo = extern struct {
|
||||
chain: ChainedStructOut,
|
||||
shared_event: *anyopaque,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreOpaqueFDDescriptor = extern struct {
|
||||
chain: ChainedStruct,
|
||||
handle: c_int,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreOpaqueFDExportInfo = extern struct {
|
||||
chain: ChainedStructOut,
|
||||
handle: c_int,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreSyncFDDescriptor = extern struct {
|
||||
chain: ChainedStruct,
|
||||
handle: c_int,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreSyncFDExportInfo = extern struct {
|
||||
chain: ChainedStructOut,
|
||||
handle: c_int,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreZirconHandleDescriptor = extern struct {
|
||||
chain: ChainedStruct,
|
||||
handle: u32,
|
||||
};
|
||||
|
||||
pub const VkSemaphoreZirconHandleExportInfo = extern struct {
|
||||
chain: ChainedStructOut,
|
||||
handle: u32,
|
||||
};
|
||||
};
|
||||
124
src/sysgpu/sysgpu/shared_texture_memory.zig
Normal file
124
src/sysgpu/sysgpu/shared_texture_memory.zig
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const Texture = @import("texture.zig").Texture;
|
||||
const Bool32 = @import("main.zig").Bool32;
|
||||
const Extent3D = @import("main.zig").Extent3D;
|
||||
const SharedFence = @import("shared_fence.zig").SharedFence;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const ChainedStructOut = @import("main.zig").ChainedStructOut;
|
||||
|
||||
pub const SharedTextureMemory = opaque {
|
||||
pub const Properties = extern struct {
|
||||
next_in_chain: *const ChainedStruct,
|
||||
usage: Texture.UsageFlags,
|
||||
size: Extent3D,
|
||||
format: Texture.Format,
|
||||
};
|
||||
|
||||
pub const VkImageDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_descriptor },
|
||||
vk_format: i32,
|
||||
vk_usage_flags: Texture.UsageFlags,
|
||||
vk_extent3D: Extent3D,
|
||||
};
|
||||
|
||||
pub const AHardwareBufferDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_a_hardware_buffer_descriptor },
|
||||
handle: *anyopaque,
|
||||
};
|
||||
|
||||
pub const BeginAccessDescriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
vk_image_layout_begin_state: *const VkImageLayoutBeginState,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
initialized: Bool32,
|
||||
fence_count: usize,
|
||||
fences: *const SharedFence,
|
||||
signaled_values: *const u64,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
a_hardware_buffer_descriptor: *const AHardwareBufferDescriptor,
|
||||
dma_buf_descriptor: *const DmaBufDescriptor,
|
||||
dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor,
|
||||
egl_image_descriptor: *const EGLImageDescriptor,
|
||||
io_surface_descriptor: *const IOSurfaceDescriptor,
|
||||
opaque_fd_descriptor: *const OpaqueFDDescriptor,
|
||||
vk_dedicated_allocation_descriptor: *const VkDedicatedAllocationDescriptor,
|
||||
zircon_handle_descriptor: *const ZirconHandleDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*]const u8,
|
||||
};
|
||||
|
||||
pub const DmaBufDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dma_buf_descriptor },
|
||||
memory_fd: c_int,
|
||||
allocation_size: u64,
|
||||
drm_modifier: u64,
|
||||
plane_count: usize,
|
||||
plane_offsets: *const u64,
|
||||
plane_strides: *const u32,
|
||||
};
|
||||
|
||||
pub const DXGISharedHandleDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dxgi_shared_handle_descriptor },
|
||||
handle: *anyopaque,
|
||||
};
|
||||
|
||||
pub const EGLImageDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_egl_image_descriptor },
|
||||
image: *anyopaque,
|
||||
};
|
||||
|
||||
pub const EndAccessState = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
vk_image_layout_end_state: *const VkImageLayoutEndState,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
initialized: Bool32,
|
||||
fence_count: usize,
|
||||
fences: *const SharedFence,
|
||||
signaled_values: *const u64,
|
||||
};
|
||||
|
||||
pub const IOSurfaceDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_io_surface_descriptor },
|
||||
ioSurface: *anyopaque,
|
||||
};
|
||||
|
||||
pub const OpaqueFDDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_opaque_fd_descriptor },
|
||||
memory_fd: c_int,
|
||||
allocation_size: u64,
|
||||
};
|
||||
|
||||
pub const VkDedicatedAllocationDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_dedicated_allocation_descriptor },
|
||||
dedicated_allocation: Bool32,
|
||||
};
|
||||
|
||||
pub const VkImageLayoutBeginState = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_begin_state },
|
||||
old_layout: i32,
|
||||
new_layout: i32,
|
||||
};
|
||||
|
||||
pub const VkImageLayoutEndState = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_end_state },
|
||||
old_layout: i32,
|
||||
new_layout: i32,
|
||||
};
|
||||
|
||||
pub const ZirconHandleDescriptor = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_zircon_handle_descriptor },
|
||||
memory_fd: u32,
|
||||
allocation_size: u64,
|
||||
};
|
||||
};
|
||||
72
src/sysgpu/sysgpu/surface.zig
Normal file
72
src/sysgpu/sysgpu/surface.zig
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const Surface = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
from_android_native_window: *const DescriptorFromAndroidNativeWindow,
|
||||
from_canvas_html_selector: *const DescriptorFromCanvasHTMLSelector,
|
||||
from_metal_layer: *const DescriptorFromMetalLayer,
|
||||
from_wayland_surface: *const DescriptorFromWaylandSurface,
|
||||
from_windows_core_window: *const DescriptorFromWindowsCoreWindow,
|
||||
from_windows_hwnd: *const DescriptorFromWindowsHWND,
|
||||
from_windows_swap_chain_panel: *const DescriptorFromWindowsSwapChainPanel,
|
||||
from_xlib_window: *const DescriptorFromXlibWindow,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
};
|
||||
|
||||
pub const DescriptorFromAndroidNativeWindow = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_android_native_window },
|
||||
window: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromCanvasHTMLSelector = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_canvas_html_selector },
|
||||
selector: [*:0]const u8,
|
||||
};
|
||||
|
||||
pub const DescriptorFromMetalLayer = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_metal_layer },
|
||||
layer: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromWaylandSurface = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_wayland_surface },
|
||||
display: *anyopaque,
|
||||
surface: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromWindowsCoreWindow = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_core_window },
|
||||
core_window: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromWindowsHWND = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_hwnd },
|
||||
hinstance: *anyopaque,
|
||||
hwnd: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromWindowsSwapChainPanel = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_swap_chain_panel },
|
||||
swap_chain_panel: *anyopaque,
|
||||
};
|
||||
|
||||
pub const DescriptorFromXlibWindow = extern struct {
|
||||
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_xlib_window },
|
||||
display: *anyopaque,
|
||||
window: u32,
|
||||
};
|
||||
|
||||
pub inline fn reference(surface: *Surface) void {
|
||||
Impl.surfaceReference(surface);
|
||||
}
|
||||
|
||||
pub inline fn release(surface: *Surface) void {
|
||||
Impl.surfaceRelease(surface);
|
||||
}
|
||||
};
|
||||
37
src/sysgpu/sysgpu/swap_chain.zig
Normal file
37
src/sysgpu/sysgpu/swap_chain.zig
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const PresentMode = @import("main.zig").PresentMode;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
const TextureView = @import("texture_view.zig").TextureView;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
|
||||
pub const SwapChain = opaque {
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
usage: Texture.UsageFlags,
|
||||
format: Texture.Format,
|
||||
width: u32,
|
||||
height: u32,
|
||||
present_mode: PresentMode,
|
||||
};
|
||||
|
||||
pub inline fn getCurrentTexture(swap_chain: *SwapChain) ?*Texture {
|
||||
return Impl.swapChainGetCurrentTexture(swap_chain);
|
||||
}
|
||||
|
||||
pub inline fn getCurrentTextureView(swap_chain: *SwapChain) ?*TextureView {
|
||||
return Impl.swapChainGetCurrentTextureView(swap_chain);
|
||||
}
|
||||
|
||||
pub inline fn present(swap_chain: *SwapChain) void {
|
||||
Impl.swapChainPresent(swap_chain);
|
||||
}
|
||||
|
||||
pub inline fn reference(swap_chain: *SwapChain) void {
|
||||
Impl.swapChainReference(swap_chain);
|
||||
}
|
||||
|
||||
pub inline fn release(swap_chain: *SwapChain) void {
|
||||
Impl.swapChainRelease(swap_chain);
|
||||
}
|
||||
};
|
||||
266
src/sysgpu/sysgpu/texture.zig
Normal file
266
src/sysgpu/sysgpu/texture.zig
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
const std = @import("std");
|
||||
const Bool32 = @import("main.zig").Bool32;
|
||||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const TextureView = @import("texture_view.zig").TextureView;
|
||||
const Extent3D = @import("main.zig").Extent3D;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const types = @import("main.zig");
|
||||
const dawn = @import("dawn.zig");
|
||||
|
||||
pub const Texture = opaque {
|
||||
pub const Aspect = enum(u32) {
|
||||
all = 0x00000000,
|
||||
stencil_only = 0x00000001,
|
||||
depth_only = 0x00000002,
|
||||
plane0_only = 0x00000003,
|
||||
plane1_only = 0x00000004,
|
||||
};
|
||||
|
||||
pub const Dimension = enum(u32) {
|
||||
dimension_1d = 0x00000000,
|
||||
dimension_2d = 0x00000001,
|
||||
dimension_3d = 0x00000002,
|
||||
};
|
||||
|
||||
pub const Format = enum(u32) {
|
||||
undefined = 0x00000000,
|
||||
r8_unorm = 0x00000001,
|
||||
r8_snorm = 0x00000002,
|
||||
r8_uint = 0x00000003,
|
||||
r8_sint = 0x00000004,
|
||||
r16_uint = 0x00000005,
|
||||
r16_sint = 0x00000006,
|
||||
r16_float = 0x00000007,
|
||||
rg8_unorm = 0x00000008,
|
||||
rg8_snorm = 0x00000009,
|
||||
rg8_uint = 0x0000000a,
|
||||
rg8_sint = 0x0000000b,
|
||||
r32_float = 0x0000000c,
|
||||
r32_uint = 0x0000000d,
|
||||
r32_sint = 0x0000000e,
|
||||
rg16_uint = 0x0000000f,
|
||||
rg16_sint = 0x00000010,
|
||||
rg16_float = 0x00000011,
|
||||
rgba8_unorm = 0x00000012,
|
||||
rgba8_unorm_srgb = 0x00000013,
|
||||
rgba8_snorm = 0x00000014,
|
||||
rgba8_uint = 0x00000015,
|
||||
rgba8_sint = 0x00000016,
|
||||
bgra8_unorm = 0x00000017,
|
||||
bgra8_unorm_srgb = 0x00000018,
|
||||
rgb10_a2_unorm = 0x00000019,
|
||||
rg11_b10_ufloat = 0x0000001a,
|
||||
rgb9_e5_ufloat = 0x0000001b,
|
||||
rg32_float = 0x0000001c,
|
||||
rg32_uint = 0x0000001d,
|
||||
rg32_sint = 0x0000001e,
|
||||
rgba16_uint = 0x0000001f,
|
||||
rgba16_sint = 0x00000020,
|
||||
rgba16_float = 0x00000021,
|
||||
rgba32_float = 0x00000022,
|
||||
rgba32_uint = 0x00000023,
|
||||
rgba32_sint = 0x00000024,
|
||||
stencil8 = 0x00000025,
|
||||
depth16_unorm = 0x00000026,
|
||||
depth24_plus = 0x00000027,
|
||||
depth24_plus_stencil8 = 0x00000028,
|
||||
depth32_float = 0x00000029,
|
||||
depth32_float_stencil8 = 0x0000002a,
|
||||
bc1_rgba_unorm = 0x0000002b,
|
||||
bc1_rgba_unorm_srgb = 0x0000002c,
|
||||
bc2_rgba_unorm = 0x0000002d,
|
||||
bc2_rgba_unorm_srgb = 0x0000002e,
|
||||
bc3_rgba_unorm = 0x0000002f,
|
||||
bc3_rgba_unorm_srgb = 0x00000030,
|
||||
bc4_runorm = 0x00000031,
|
||||
bc4_rsnorm = 0x00000032,
|
||||
bc5_rg_unorm = 0x00000033,
|
||||
bc5_rg_snorm = 0x00000034,
|
||||
bc6_hrgb_ufloat = 0x00000035,
|
||||
bc6_hrgb_float = 0x00000036,
|
||||
bc7_rgba_unorm = 0x00000037,
|
||||
bc7_rgba_unorm_srgb = 0x00000038,
|
||||
etc2_rgb8_unorm = 0x00000039,
|
||||
etc2_rgb8_unorm_srgb = 0x0000003a,
|
||||
etc2_rgb8_a1_unorm = 0x0000003b,
|
||||
etc2_rgb8_a1_unorm_srgb = 0x0000003c,
|
||||
etc2_rgba8_unorm = 0x0000003d,
|
||||
etc2_rgba8_unorm_srgb = 0x0000003e,
|
||||
eacr11_unorm = 0x0000003f,
|
||||
eacr11_snorm = 0x00000040,
|
||||
eacrg11_unorm = 0x00000041,
|
||||
eacrg11_snorm = 0x00000042,
|
||||
astc4x4_unorm = 0x00000043,
|
||||
astc4x4_unorm_srgb = 0x00000044,
|
||||
astc5x4_unorm = 0x00000045,
|
||||
astc5x4_unorm_srgb = 0x00000046,
|
||||
astc5x5_unorm = 0x00000047,
|
||||
astc5x5_unorm_srgb = 0x00000048,
|
||||
astc6x5_unorm = 0x00000049,
|
||||
astc6x5_unorm_srgb = 0x0000004a,
|
||||
astc6x6_unorm = 0x0000004b,
|
||||
astc6x6_unorm_srgb = 0x0000004c,
|
||||
astc8x5_unorm = 0x0000004d,
|
||||
astc8x5_unorm_srgb = 0x0000004e,
|
||||
astc8x6_unorm = 0x0000004f,
|
||||
astc8x6_unorm_srgb = 0x00000050,
|
||||
astc8x8_unorm = 0x00000051,
|
||||
astc8x8_unorm_srgb = 0x00000052,
|
||||
astc10x5_unorm = 0x00000053,
|
||||
astc10x5_unorm_srgb = 0x00000054,
|
||||
astc10x6_unorm = 0x00000055,
|
||||
astc10x6_unorm_srgb = 0x00000056,
|
||||
astc10x8_unorm = 0x00000057,
|
||||
astc10x8_unorm_srgb = 0x00000058,
|
||||
astc10x10_unorm = 0x00000059,
|
||||
astc10x10_unorm_srgb = 0x0000005a,
|
||||
astc12x10_unorm = 0x0000005b,
|
||||
astc12x10_unorm_srgb = 0x0000005c,
|
||||
astc12x12_unorm = 0x0000005d,
|
||||
astc12x12_unorm_srgb = 0x0000005e,
|
||||
r8_bg8_biplanar420_unorm = 0x0000005f,
|
||||
};
|
||||
|
||||
pub const SampleType = enum(u32) {
|
||||
undefined = 0x00000000,
|
||||
float = 0x00000001,
|
||||
unfilterable_float = 0x00000002,
|
||||
depth = 0x00000003,
|
||||
sint = 0x00000004,
|
||||
uint = 0x00000005,
|
||||
};
|
||||
|
||||
pub const UsageFlags = packed struct(u32) {
|
||||
copy_src: bool = false,
|
||||
copy_dst: bool = false,
|
||||
texture_binding: bool = false,
|
||||
storage_binding: bool = false,
|
||||
render_attachment: bool = false,
|
||||
transient_attachment: bool = false,
|
||||
|
||||
_padding: u26 = 0,
|
||||
|
||||
comptime {
|
||||
std.debug.assert(
|
||||
@sizeOf(@This()) == @sizeOf(u32) and
|
||||
@bitSizeOf(@This()) == @bitSizeOf(u32),
|
||||
);
|
||||
}
|
||||
|
||||
pub const none = UsageFlags{};
|
||||
|
||||
pub fn equal(a: UsageFlags, b: UsageFlags) bool {
|
||||
return @as(u6, @truncate(@as(u32, @bitCast(a)))) == @as(u6, @truncate(@as(u32, @bitCast(b))));
|
||||
}
|
||||
};
|
||||
|
||||
pub const BindingLayout = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
sample_type: SampleType = .undefined,
|
||||
view_dimension: TextureView.Dimension = .dimension_undefined,
|
||||
multisampled: Bool32 = .false,
|
||||
};
|
||||
|
||||
pub const DataLayout = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
offset: u64 = 0,
|
||||
bytes_per_row: u32 = types.copy_stride_undefined,
|
||||
rows_per_image: u32 = types.copy_stride_undefined,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
pub const NextInChain = extern union {
|
||||
generic: ?*const ChainedStruct,
|
||||
dawn_texture_internal_usage_descriptor: *const dawn.TextureInternalUsageDescriptor,
|
||||
};
|
||||
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
usage: UsageFlags,
|
||||
dimension: Dimension = .dimension_2d,
|
||||
size: Extent3D,
|
||||
format: Format,
|
||||
mip_level_count: u32 = 1,
|
||||
sample_count: u32 = 1,
|
||||
view_format_count: usize = 0,
|
||||
view_formats: ?[*]const Format = null,
|
||||
|
||||
/// Provides a slightly friendlier Zig API to initialize this structure.
|
||||
pub inline fn init(v: struct {
|
||||
next_in_chain: NextInChain = .{ .generic = null },
|
||||
label: ?[*:0]const u8 = null,
|
||||
usage: UsageFlags,
|
||||
dimension: Dimension = .dimension_2d,
|
||||
size: Extent3D,
|
||||
format: Format,
|
||||
mip_level_count: u32 = 1,
|
||||
sample_count: u32 = 1,
|
||||
view_formats: ?[]const Format = null,
|
||||
}) Descriptor {
|
||||
return .{
|
||||
.next_in_chain = v.next_in_chain,
|
||||
.label = v.label,
|
||||
.usage = v.usage,
|
||||
.dimension = v.dimension,
|
||||
.size = v.size,
|
||||
.format = v.format,
|
||||
.mip_level_count = v.mip_level_count,
|
||||
.sample_count = v.sample_count,
|
||||
.view_format_count = if (v.view_formats) |e| e.len else 0,
|
||||
.view_formats = if (v.view_formats) |e| e.ptr else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn createView(texture: *Texture, descriptor: ?*const TextureView.Descriptor) *TextureView {
|
||||
return Impl.textureCreateView(texture, descriptor);
|
||||
}
|
||||
|
||||
pub inline fn destroy(texture: *Texture) void {
|
||||
Impl.textureDestroy(texture);
|
||||
}
|
||||
|
||||
pub inline fn getDepthOrArrayLayers(texture: *Texture) u32 {
|
||||
return Impl.textureGetDepthOrArrayLayers(texture);
|
||||
}
|
||||
|
||||
pub inline fn getDimension(texture: *Texture) Dimension {
|
||||
return Impl.textureGetDimension(texture);
|
||||
}
|
||||
|
||||
pub inline fn getFormat(texture: *Texture) Format {
|
||||
return Impl.textureGetFormat(texture);
|
||||
}
|
||||
|
||||
pub inline fn getHeight(texture: *Texture) u32 {
|
||||
return Impl.textureGetHeight(texture);
|
||||
}
|
||||
|
||||
pub inline fn getMipLevelCount(texture: *Texture) u32 {
|
||||
return Impl.textureGetMipLevelCount(texture);
|
||||
}
|
||||
|
||||
pub inline fn getSampleCount(texture: *Texture) u32 {
|
||||
return Impl.textureGetSampleCount(texture);
|
||||
}
|
||||
|
||||
pub inline fn getUsage(texture: *Texture) UsageFlags {
|
||||
return Impl.textureGetUsage(texture);
|
||||
}
|
||||
|
||||
pub inline fn getWidth(texture: *Texture) u32 {
|
||||
return Impl.textureGetWidth(texture);
|
||||
}
|
||||
|
||||
pub inline fn setLabel(texture: *Texture, label: [*:0]const u8) void {
|
||||
Impl.textureSetLabel(texture, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(texture: *Texture) void {
|
||||
Impl.textureReference(texture);
|
||||
}
|
||||
|
||||
pub inline fn release(texture: *Texture) void {
|
||||
Impl.textureRelease(texture);
|
||||
}
|
||||
};
|
||||
40
src/sysgpu/sysgpu/texture_view.zig
Normal file
40
src/sysgpu/sysgpu/texture_view.zig
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
const ChainedStruct = @import("main.zig").ChainedStruct;
|
||||
const Texture = @import("texture.zig").Texture;
|
||||
const Impl = @import("interface.zig").Impl;
|
||||
const types = @import("main.zig");
|
||||
|
||||
pub const TextureView = opaque {
|
||||
pub const Dimension = enum(u32) {
|
||||
dimension_undefined = 0x00000000,
|
||||
dimension_1d = 0x00000001,
|
||||
dimension_2d = 0x00000002,
|
||||
dimension_2d_array = 0x00000003,
|
||||
dimension_cube = 0x00000004,
|
||||
dimension_cube_array = 0x00000005,
|
||||
dimension_3d = 0x00000006,
|
||||
};
|
||||
|
||||
pub const Descriptor = extern struct {
|
||||
next_in_chain: ?*const ChainedStruct = null,
|
||||
label: ?[*:0]const u8 = null,
|
||||
format: Texture.Format = .undefined,
|
||||
dimension: Dimension = .dimension_undefined,
|
||||
base_mip_level: u32 = 0,
|
||||
mip_level_count: u32 = types.mip_level_count_undefined,
|
||||
base_array_layer: u32 = 0,
|
||||
array_layer_count: u32 = types.array_layer_count_undefined,
|
||||
aspect: Texture.Aspect = .all,
|
||||
};
|
||||
|
||||
pub inline fn setLabel(texture_view: *TextureView, label: [*:0]const u8) void {
|
||||
Impl.textureViewSetLabel(texture_view, label);
|
||||
}
|
||||
|
||||
pub inline fn reference(texture_view: *TextureView) void {
|
||||
Impl.textureViewReference(texture_view);
|
||||
}
|
||||
|
||||
pub inline fn release(texture_view: *TextureView) void {
|
||||
Impl.textureViewRelease(texture_view);
|
||||
}
|
||||
};
|
||||
696
src/sysgpu/tools/gen_spirv_spec.zig
Normal file
696
src/sysgpu/tools/gen_spirv_spec.zig
Normal file
|
|
@ -0,0 +1,696 @@
|
|||
//! Borrowed from Zig compiler codebase with changes.
|
||||
//! Licensed under LICENSE-ZIG
|
||||
|
||||
const std = @import("std");
|
||||
const g = @import("spirv/grammar.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const ExtendedStructSet = std.StringHashMap(void);
|
||||
|
||||
pub fn main() !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
if (args.len != 2) {
|
||||
usageAndExit(std.io.getStdErr(), args[0], 1);
|
||||
}
|
||||
|
||||
const spec_path = args[1];
|
||||
const spec = try std.fs.cwd().readFileAlloc(allocator, spec_path, std.math.maxInt(usize));
|
||||
|
||||
// Required for json parsing.
|
||||
@setEvalBranchQuota(10000);
|
||||
|
||||
var scanner = std.json.Scanner.initCompleteInput(allocator, spec);
|
||||
var diagnostics = std.json.Diagnostics{};
|
||||
scanner.enableDiagnostics(&diagnostics);
|
||||
const parsed = std.json.parseFromTokenSource(g.CoreRegistry, allocator, &scanner, .{}) catch |err| {
|
||||
std.debug.print("line,col: {},{}\n", .{ diagnostics.getLine(), diagnostics.getColumn() });
|
||||
return err;
|
||||
};
|
||||
|
||||
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
||||
try render(bw.writer(), allocator, parsed.value);
|
||||
try bw.flush();
|
||||
}
|
||||
|
||||
/// Returns a set with types that require an extra struct for the `Instruction` interface
|
||||
/// to the spir-v spec, or whether the original type can be used.
|
||||
fn extendedStructs(
|
||||
arena: Allocator,
|
||||
kinds: []const g.OperandKind,
|
||||
) !ExtendedStructSet {
|
||||
var map = ExtendedStructSet.init(arena);
|
||||
try map.ensureTotalCapacity(@intCast(kinds.len));
|
||||
|
||||
for (kinds) |kind| {
|
||||
const enumerants = kind.enumerants orelse continue;
|
||||
|
||||
for (enumerants) |enumerant| {
|
||||
if (enumerant.parameters.len > 0) {
|
||||
break;
|
||||
}
|
||||
} else continue;
|
||||
|
||||
map.putAssumeCapacity(kind.kind, {});
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// Return a score for a particular priority. Duplicate instruction/operand enum values are
|
||||
// removed by picking the tag with the lowest score to keep, and by making an alias for the
|
||||
// other. Note that the tag does not need to be just a tag at this point, in which case it
|
||||
// gets the lowest score automatically anyway.
|
||||
fn tagPriorityScore(tag: []const u8) usize {
|
||||
if (tag.len == 0) {
|
||||
return 1;
|
||||
} else if (std.mem.eql(u8, tag, "EXT")) {
|
||||
return 2;
|
||||
} else if (std.mem.eql(u8, tag, "KHR")) {
|
||||
return 3;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void {
|
||||
try writer.writeAll(
|
||||
\\//! Borrowed from Zig compiler codebase with changes.
|
||||
\\//! Licensed under LICENSE-ZIG
|
||||
\\//!
|
||||
\\//! This file is auto-generated by tools/gen_spirv_spec.zig.
|
||||
\\
|
||||
\\pub const Version = packed struct(Word) {
|
||||
\\ padding: u8 = 0,
|
||||
\\ minor: u8,
|
||||
\\ major: u8,
|
||||
\\ padding0: u8 = 0,
|
||||
\\
|
||||
\\ pub fn toWord(self: @This()) Word {
|
||||
\\ return @bitCast(self);
|
||||
\\ }
|
||||
\\};
|
||||
\\
|
||||
\\pub const Word = u32;
|
||||
\\pub const IdResult = struct{
|
||||
\\ id: Word,
|
||||
\\};
|
||||
\\pub const IdResultType = IdResult;
|
||||
\\pub const IdRef = IdResult;
|
||||
\\
|
||||
\\pub const IdMemorySemantics = IdRef;
|
||||
\\pub const IdScope = IdRef;
|
||||
\\
|
||||
\\pub const LiteralInteger = Word;
|
||||
\\pub const LiteralString = []const u8;
|
||||
\\pub const LiteralContextDependentNumber = union(enum) {
|
||||
\\ int32: i32,
|
||||
\\ uint32: u32,
|
||||
\\ int64: i64,
|
||||
\\ uint64: u64,
|
||||
\\ float32: f32,
|
||||
\\ float64: f64,
|
||||
\\};
|
||||
\\pub const LiteralExtInstInteger = struct{ inst: Word };
|
||||
\\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode };
|
||||
\\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef };
|
||||
\\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger };
|
||||
\\pub const PairIdRefIdRef = [2]IdRef;
|
||||
\\
|
||||
\\pub const Quantifier = enum {
|
||||
\\ required,
|
||||
\\ optional,
|
||||
\\ variadic,
|
||||
\\};
|
||||
\\
|
||||
\\pub const Operand = struct {
|
||||
\\ kind: OperandKind,
|
||||
\\ quantifier: Quantifier,
|
||||
\\};
|
||||
\\
|
||||
\\pub const OperandCategory = enum {
|
||||
\\ bit_enum,
|
||||
\\ value_enum,
|
||||
\\ id,
|
||||
\\ literal,
|
||||
\\ composite,
|
||||
\\};
|
||||
\\
|
||||
\\pub const Enumerant = struct {
|
||||
\\ name: []const u8,
|
||||
\\ value: Word,
|
||||
\\ parameters: []const OperandKind,
|
||||
\\};
|
||||
\\
|
||||
\\
|
||||
);
|
||||
|
||||
try writer.print(
|
||||
\\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};
|
||||
\\pub const magic_number: Word = {s};
|
||||
\\
|
||||
\\
|
||||
,
|
||||
.{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number },
|
||||
);
|
||||
|
||||
const extended_structs = try extendedStructs(allocator, registry.operand_kinds);
|
||||
try renderClass(writer, allocator, registry.instructions);
|
||||
try renderOperandKind(writer, registry.operand_kinds);
|
||||
try renderOpcodes(writer, allocator, registry.instructions, extended_structs);
|
||||
try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs);
|
||||
}
|
||||
|
||||
fn renderClass(writer: anytype, allocator: Allocator, instructions: []const g.Instruction) !void {
|
||||
var class_map = std.StringArrayHashMap(void).init(allocator);
|
||||
|
||||
for (instructions) |inst| {
|
||||
if (std.mem.eql(u8, inst.class.?, "@exclude")) {
|
||||
continue;
|
||||
}
|
||||
try class_map.put(inst.class.?, {});
|
||||
}
|
||||
|
||||
try writer.writeAll("pub const Class = enum {\n");
|
||||
for (class_map.keys()) |class| {
|
||||
try renderInstructionClass(writer, class);
|
||||
try writer.writeAll(",\n");
|
||||
}
|
||||
try writer.writeAll("};\n");
|
||||
}
|
||||
|
||||
fn renderInstructionClass(writer: anytype, class: []const u8) !void {
|
||||
// Just assume that these wont clobber zig builtin types.
|
||||
var prev_was_sep = true;
|
||||
for (class) |c| {
|
||||
switch (c) {
|
||||
'-', '_' => prev_was_sep = true,
|
||||
else => if (prev_was_sep) {
|
||||
try writer.writeByte(std.ascii.toUpper(c));
|
||||
prev_was_sep = false;
|
||||
} else {
|
||||
try writer.writeByte(std.ascii.toLower(c));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn renderOperandKind(writer: anytype, operands: []const g.OperandKind) !void {
|
||||
try writer.writeAll("pub const OperandKind = enum {\n");
|
||||
for (operands) |operand| {
|
||||
try writer.print("{},\n", .{std.zig.fmtId(operand.kind)});
|
||||
}
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\pub fn category(self: OperandKind) OperandCategory {
|
||||
\\return switch (self) {
|
||||
\\
|
||||
);
|
||||
for (operands) |operand| {
|
||||
const cat = switch (operand.category) {
|
||||
.BitEnum => "bit_enum",
|
||||
.ValueEnum => "value_enum",
|
||||
.Id => "id",
|
||||
.Literal => "literal",
|
||||
.Composite => "composite",
|
||||
};
|
||||
try writer.print(".{} => .{s},\n", .{ std.zig.fmtId(operand.kind), cat });
|
||||
}
|
||||
try writer.writeAll(
|
||||
\\};
|
||||
\\}
|
||||
\\pub fn enumerants(self: OperandKind) []const Enumerant {
|
||||
\\return switch (self) {
|
||||
\\
|
||||
);
|
||||
for (operands) |operand| {
|
||||
switch (operand.category) {
|
||||
.BitEnum, .ValueEnum => {},
|
||||
else => {
|
||||
try writer.print(".{} => unreachable,\n", .{std.zig.fmtId(operand.kind)});
|
||||
continue;
|
||||
},
|
||||
}
|
||||
|
||||
try writer.print(".{} => &[_]Enumerant{{", .{std.zig.fmtId(operand.kind)});
|
||||
for (operand.enumerants.?) |enumerant| {
|
||||
if (enumerant.value == .bitflag and std.mem.eql(u8, enumerant.enumerant, "None")) {
|
||||
continue;
|
||||
}
|
||||
try renderEnumerant(writer, enumerant);
|
||||
try writer.writeAll(",");
|
||||
}
|
||||
try writer.writeAll("},\n");
|
||||
}
|
||||
try writer.writeAll("};\n}\n};\n");
|
||||
}
|
||||
|
||||
fn renderEnumerant(writer: anytype, enumerant: g.Enumerant) !void {
|
||||
try writer.print(".{{.name = \"{s}\", .value = ", .{enumerant.enumerant});
|
||||
switch (enumerant.value) {
|
||||
.bitflag => |flag| try writer.writeAll(flag),
|
||||
.int => |int| try writer.print("{}", .{int}),
|
||||
}
|
||||
try writer.writeAll(", .parameters = &[_]OperandKind{");
|
||||
for (enumerant.parameters, 0..) |param, i| {
|
||||
if (i != 0)
|
||||
try writer.writeAll(", ");
|
||||
// Note, param.quantifier will always be one.
|
||||
try writer.print(".{}", .{std.zig.fmtId(param.kind)});
|
||||
}
|
||||
try writer.writeAll("}}");
|
||||
}
|
||||
|
||||
fn renderOpcodes(
|
||||
writer: anytype,
|
||||
allocator: Allocator,
|
||||
instructions: []const g.Instruction,
|
||||
extended_structs: ExtendedStructSet,
|
||||
) !void {
|
||||
var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator);
|
||||
try inst_map.ensureTotalCapacity(instructions.len);
|
||||
|
||||
var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator);
|
||||
try aliases.ensureTotalCapacity(instructions.len);
|
||||
|
||||
for (instructions, 0..) |inst, i| {
|
||||
if (std.mem.eql(u8, inst.class.?, "@exclude")) {
|
||||
continue;
|
||||
}
|
||||
const result = inst_map.getOrPutAssumeCapacity(inst.opcode);
|
||||
if (!result.found_existing) {
|
||||
result.value_ptr.* = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = instructions[result.value_ptr.*];
|
||||
|
||||
const tag_index = std.mem.indexOfDiff(u8, inst.opname, existing.opname).?;
|
||||
const inst_priority = tagPriorityScore(inst.opname[tag_index..]);
|
||||
const existing_priority = tagPriorityScore(existing.opname[tag_index..]);
|
||||
|
||||
if (inst_priority < existing_priority) {
|
||||
aliases.appendAssumeCapacity(.{ .inst = result.value_ptr.*, .alias = i });
|
||||
result.value_ptr.* = i;
|
||||
} else {
|
||||
aliases.appendAssumeCapacity(.{ .inst = i, .alias = result.value_ptr.* });
|
||||
}
|
||||
}
|
||||
|
||||
const instructions_indices = inst_map.values();
|
||||
|
||||
try writer.writeAll("pub const Opcode = enum(u16) {\n");
|
||||
for (instructions_indices) |i| {
|
||||
const inst = instructions[i];
|
||||
try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode });
|
||||
}
|
||||
|
||||
try writer.writeByte('\n');
|
||||
|
||||
for (aliases.items) |alias| {
|
||||
try writer.print("pub const {} = Opcode.{};\n", .{
|
||||
std.zig.fmtId(instructions[alias.inst].opname),
|
||||
std.zig.fmtId(instructions[alias.alias].opname),
|
||||
});
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\pub fn Operands(comptime self: Opcode) type {
|
||||
\\return switch (self) {
|
||||
\\
|
||||
);
|
||||
|
||||
for (instructions_indices) |i| {
|
||||
const inst = instructions[i];
|
||||
try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs);
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\};
|
||||
\\}
|
||||
\\pub fn operands(self: Opcode) []const Operand {
|
||||
\\return switch (self) {
|
||||
\\
|
||||
);
|
||||
|
||||
for (instructions_indices) |i| {
|
||||
const inst = instructions[i];
|
||||
try writer.print(".{} => &[_]Operand{{", .{std.zig.fmtId(inst.opname)});
|
||||
for (inst.operands) |operand| {
|
||||
const quantifier = if (operand.quantifier) |q|
|
||||
switch (q) {
|
||||
.@"?" => "optional",
|
||||
.@"*" => "variadic",
|
||||
}
|
||||
else
|
||||
"required";
|
||||
|
||||
try writer.print(".{{.kind = .{s}, .quantifier = .{s}}},", .{ operand.kind, quantifier });
|
||||
}
|
||||
try writer.writeAll("},\n");
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\};
|
||||
\\}
|
||||
\\pub fn class(self: Opcode) Class {
|
||||
\\return switch (self) {
|
||||
\\
|
||||
);
|
||||
|
||||
for (instructions_indices) |i| {
|
||||
const inst = instructions[i];
|
||||
try writer.print(".{} => .", .{std.zig.fmtId(inst.opname)});
|
||||
try renderInstructionClass(writer, inst.class.?);
|
||||
try writer.writeAll(",\n");
|
||||
}
|
||||
|
||||
try writer.writeAll("};\n}\n};\n");
|
||||
}
|
||||
|
||||
fn renderOperandKinds(
|
||||
writer: anytype,
|
||||
allocator: Allocator,
|
||||
kinds: []const g.OperandKind,
|
||||
extended_structs: ExtendedStructSet,
|
||||
) !void {
|
||||
for (kinds) |kind| {
|
||||
switch (kind.category) {
|
||||
.ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs),
|
||||
.BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn renderValueEnum(
|
||||
writer: anytype,
|
||||
allocator: Allocator,
|
||||
enumeration: g.OperandKind,
|
||||
extended_structs: ExtendedStructSet,
|
||||
) !void {
|
||||
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
|
||||
|
||||
var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator);
|
||||
try enum_map.ensureTotalCapacity(enumerants.len);
|
||||
|
||||
var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator);
|
||||
try aliases.ensureTotalCapacity(enumerants.len);
|
||||
|
||||
for (enumerants, 0..) |enumerant, i| {
|
||||
const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int);
|
||||
if (!result.found_existing) {
|
||||
result.value_ptr.* = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = enumerants[result.value_ptr.*];
|
||||
|
||||
const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?;
|
||||
const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
|
||||
const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]);
|
||||
|
||||
if (enum_priority < existing_priority) {
|
||||
aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i });
|
||||
result.value_ptr.* = i;
|
||||
} else {
|
||||
aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* });
|
||||
}
|
||||
}
|
||||
|
||||
const enum_indices = enum_map.values();
|
||||
|
||||
try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)});
|
||||
|
||||
for (enum_indices) |i| {
|
||||
const enumerant = enumerants[i];
|
||||
if (enumerant.value != .int) return error.InvalidRegistry;
|
||||
|
||||
try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int });
|
||||
}
|
||||
|
||||
try writer.writeByte('\n');
|
||||
|
||||
for (aliases.items) |alias| {
|
||||
try writer.print("pub const {} = {}.{};\n", .{
|
||||
std.zig.fmtId(enumerants[alias.enumerant].enumerant),
|
||||
std.zig.fmtId(enumeration.kind),
|
||||
std.zig.fmtId(enumerants[alias.alias].enumerant),
|
||||
});
|
||||
}
|
||||
|
||||
if (!extended_structs.contains(enumeration.kind)) {
|
||||
try writer.writeAll("};\n");
|
||||
return;
|
||||
}
|
||||
|
||||
try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)});
|
||||
|
||||
for (enum_indices) |i| {
|
||||
const enumerant = enumerants[i];
|
||||
try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs);
|
||||
}
|
||||
|
||||
try writer.writeAll("};\n};\n");
|
||||
}
|
||||
|
||||
fn renderBitEnum(
|
||||
writer: anytype,
|
||||
allocator: Allocator,
|
||||
enumeration: g.OperandKind,
|
||||
extended_structs: ExtendedStructSet,
|
||||
) !void {
|
||||
try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)});
|
||||
|
||||
var flags_by_bitpos = [_]?usize{null} ** 32;
|
||||
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
|
||||
|
||||
var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator);
|
||||
try aliases.ensureTotalCapacity(enumerants.len);
|
||||
|
||||
for (enumerants, 0..) |enumerant, i| {
|
||||
if (enumerant.value != .bitflag) return error.InvalidRegistry;
|
||||
const value = try parseHexInt(enumerant.value.bitflag);
|
||||
if (value == 0) {
|
||||
continue; // Skip 'none' items
|
||||
}
|
||||
|
||||
std.debug.assert(@popCount(value) == 1);
|
||||
|
||||
const bitpos = std.math.log2_int(u32, value);
|
||||
if (flags_by_bitpos[bitpos]) |*existing| {
|
||||
const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?;
|
||||
const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
|
||||
const existing_priority = tagPriorityScore(enumerants[existing.*].enumerant[tag_index..]);
|
||||
|
||||
if (enum_priority < existing_priority) {
|
||||
aliases.appendAssumeCapacity(.{ .flag = existing.*, .alias = bitpos });
|
||||
existing.* = i;
|
||||
} else {
|
||||
aliases.appendAssumeCapacity(.{ .flag = i, .alias = bitpos });
|
||||
}
|
||||
} else {
|
||||
flags_by_bitpos[bitpos] = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| {
|
||||
if (maybe_flag_index) |flag_index| {
|
||||
try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)});
|
||||
} else {
|
||||
try writer.print("_reserved_bit_{}", .{bitpos});
|
||||
}
|
||||
|
||||
try writer.writeAll(": bool = false,\n");
|
||||
}
|
||||
|
||||
try writer.writeByte('\n');
|
||||
|
||||
for (aliases.items) |alias| {
|
||||
try writer.print("pub const {}: {} = .{{.{} = true}};\n", .{
|
||||
std.zig.fmtId(enumerants[alias.flag].enumerant),
|
||||
std.zig.fmtId(enumeration.kind),
|
||||
std.zig.fmtId(enumerants[flags_by_bitpos[alias.alias].?].enumerant),
|
||||
});
|
||||
}
|
||||
|
||||
if (!extended_structs.contains(enumeration.kind)) {
|
||||
try writer.writeAll("};\n");
|
||||
return;
|
||||
}
|
||||
|
||||
try writer.print("\npub const Extended = struct {{\n", .{});
|
||||
|
||||
for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| {
|
||||
const flag_index = maybe_flag_index orelse {
|
||||
try writer.print("_reserved_bit_{}: bool = false,\n", .{bitpos});
|
||||
continue;
|
||||
};
|
||||
const enumerant = enumerants[flag_index];
|
||||
|
||||
try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs);
|
||||
}
|
||||
|
||||
try writer.writeAll("};\n};\n");
|
||||
}
|
||||
|
||||
fn renderOperand(
|
||||
writer: anytype,
|
||||
kind: enum {
|
||||
@"union",
|
||||
instruction,
|
||||
mask,
|
||||
},
|
||||
field_name: []const u8,
|
||||
parameters: []const g.Operand,
|
||||
extended_structs: ExtendedStructSet,
|
||||
) !void {
|
||||
if (kind == .instruction) {
|
||||
try writer.writeByte('.');
|
||||
}
|
||||
try writer.print("{}", .{std.zig.fmtId(field_name)});
|
||||
if (parameters.len == 0) {
|
||||
switch (kind) {
|
||||
.@"union" => try writer.writeAll(",\n"),
|
||||
.instruction => try writer.writeAll(" => void,\n"),
|
||||
.mask => try writer.writeAll(": bool = false,\n"),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind == .instruction) {
|
||||
try writer.writeAll(" => ");
|
||||
} else {
|
||||
try writer.writeAll(": ");
|
||||
}
|
||||
|
||||
if (kind == .mask) {
|
||||
try writer.writeByte('?');
|
||||
}
|
||||
|
||||
try writer.writeAll("struct{");
|
||||
|
||||
for (parameters, 0..) |param, j| {
|
||||
if (j != 0) {
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try renderFieldName(writer, parameters, j);
|
||||
try writer.writeAll(": ");
|
||||
|
||||
if (param.quantifier) |q| {
|
||||
switch (q) {
|
||||
.@"?" => try writer.writeByte('?'),
|
||||
.@"*" => try writer.writeAll("[]const "),
|
||||
}
|
||||
}
|
||||
|
||||
try writer.print("{}", .{std.zig.fmtId(param.kind)});
|
||||
|
||||
if (extended_structs.contains(param.kind)) {
|
||||
try writer.writeAll(".Extended");
|
||||
}
|
||||
|
||||
if (param.quantifier) |q| {
|
||||
switch (q) {
|
||||
.@"?" => try writer.writeAll(" = null"),
|
||||
.@"*" => try writer.writeAll(" = &.{}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll("}");
|
||||
|
||||
if (kind == .mask) {
|
||||
try writer.writeAll(" = null");
|
||||
}
|
||||
|
||||
try writer.writeAll(",\n");
|
||||
}
|
||||
|
||||
fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void {
|
||||
const operand = operands[field_index];
|
||||
|
||||
// Should be enough for all names - adjust as needed.
|
||||
var name_buffer = std.BoundedArray(u8, 64){
|
||||
.buffer = undefined,
|
||||
};
|
||||
|
||||
derive_from_kind: {
|
||||
// Operand names are often in the json encoded as "'Name'" (with two sets of quotes).
|
||||
// Additionally, some operands have ~ in them at the end (D~ref~).
|
||||
const name = std.mem.trim(u8, operand.name, "'~");
|
||||
if (name.len == 0) {
|
||||
break :derive_from_kind;
|
||||
}
|
||||
|
||||
// Some names have weird characters in them (like newlines) - skip any such ones.
|
||||
// Use the same loop to transform to snake-case.
|
||||
for (name) |c| {
|
||||
switch (c) {
|
||||
'a'...'z', '0'...'9' => try name_buffer.append(c),
|
||||
'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)),
|
||||
' ', '~' => try name_buffer.append('_'),
|
||||
else => break :derive_from_kind,
|
||||
}
|
||||
}
|
||||
|
||||
// Assume there are no duplicate 'name' fields.
|
||||
try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
|
||||
return;
|
||||
}
|
||||
|
||||
// Translate to snake case.
|
||||
name_buffer.len = 0;
|
||||
for (operand.kind, 0..) |c, i| {
|
||||
switch (c) {
|
||||
'a'...'z', '0'...'9' => try name_buffer.append(c),
|
||||
'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) {
|
||||
try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) });
|
||||
} else {
|
||||
try name_buffer.append(std.ascii.toLower(c));
|
||||
},
|
||||
else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores).
|
||||
}
|
||||
}
|
||||
|
||||
try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
|
||||
|
||||
// For fields derived from type name, there could be any amount.
|
||||
// Simply check against all other fields, and if another similar one exists, add a number.
|
||||
const need_extra_index = for (operands, 0..) |other_operand, i| {
|
||||
if (i != field_index and std.mem.eql(u8, operand.kind, other_operand.kind)) {
|
||||
break true;
|
||||
}
|
||||
} else false;
|
||||
|
||||
if (need_extra_index) {
|
||||
try writer.print("_{}", .{field_index});
|
||||
}
|
||||
}
|
||||
|
||||
fn parseHexInt(text: []const u8) !u31 {
|
||||
const prefix = "0x";
|
||||
if (!std.mem.startsWith(u8, text, prefix))
|
||||
return error.InvalidHexInt;
|
||||
return try std.fmt.parseInt(u31, text[prefix.len..], 16);
|
||||
}
|
||||
|
||||
fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn {
|
||||
file.writer().print(
|
||||
\\Usage: {s} <spirv json spec>
|
||||
\\
|
||||
\\Generates Zig bindings for a SPIR-V specification .json (either core or
|
||||
\\extinst versions). The result, printed to stdout, should be used to update
|
||||
\\files in src/codegen/spirv. Don't forget to format the output.
|
||||
\\
|
||||
\\The relevant specifications can be obtained from the SPIR-V registry:
|
||||
\\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/
|
||||
\\
|
||||
, .{arg0}) catch std.process.exit(1);
|
||||
std.process.exit(code);
|
||||
}
|
||||
110
src/sysgpu/tools/spirv/grammar.zig
Normal file
110
src/sysgpu/tools/spirv/grammar.zig
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
//! Borrowed from Zig compiler codebase with changes.
|
||||
//! Licensed under LICENSE-ZIG
|
||||
//!
|
||||
//! See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html
|
||||
//! and the files in https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/
|
||||
//! Note: Non-canonical casing in these structs used to match SPIR-V spec json.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Registry = union(enum) {
|
||||
core: CoreRegistry,
|
||||
extension: ExtensionRegistry,
|
||||
};
|
||||
|
||||
pub const CoreRegistry = struct {
|
||||
copyright: [][]const u8,
|
||||
/// Hexadecimal representation of the magic number
|
||||
magic_number: []const u8,
|
||||
major_version: u32,
|
||||
minor_version: u32,
|
||||
revision: u32,
|
||||
instruction_printing_class: []InstructionPrintingClass,
|
||||
instructions: []Instruction,
|
||||
operand_kinds: []OperandKind,
|
||||
};
|
||||
|
||||
pub const ExtensionRegistry = struct {
|
||||
copyright: [][]const u8,
|
||||
version: u32,
|
||||
revision: u32,
|
||||
instructions: []Instruction,
|
||||
operand_kinds: []OperandKind = &[_]OperandKind{},
|
||||
};
|
||||
|
||||
pub const InstructionPrintingClass = struct {
|
||||
tag: []const u8,
|
||||
heading: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub const Instruction = struct {
|
||||
opname: []const u8,
|
||||
class: ?[]const u8 = null, // Note: Only available in the core registry.
|
||||
opcode: u32,
|
||||
operands: []Operand = &[_]Operand{},
|
||||
capabilities: [][]const u8 = &[_][]const u8{},
|
||||
extensions: [][]const u8 = &[_][]const u8{},
|
||||
version: ?[]const u8 = null,
|
||||
|
||||
lastVersion: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub const Operand = struct {
|
||||
kind: []const u8,
|
||||
/// If this field is 'null', the operand is only expected once.
|
||||
quantifier: ?Quantifier = null,
|
||||
name: []const u8 = "",
|
||||
};
|
||||
|
||||
pub const Quantifier = enum {
|
||||
/// zero or once
|
||||
@"?",
|
||||
/// zero or more
|
||||
@"*",
|
||||
};
|
||||
|
||||
pub const OperandCategory = enum {
|
||||
BitEnum,
|
||||
ValueEnum,
|
||||
Id,
|
||||
Literal,
|
||||
Composite,
|
||||
};
|
||||
|
||||
pub const OperandKind = struct {
|
||||
category: OperandCategory,
|
||||
/// The name
|
||||
kind: []const u8,
|
||||
doc: ?[]const u8 = null,
|
||||
enumerants: ?[]Enumerant = null,
|
||||
bases: ?[]const []const u8 = null,
|
||||
};
|
||||
|
||||
pub const Enumerant = struct {
|
||||
enumerant: []const u8,
|
||||
value: union(enum) {
|
||||
bitflag: []const u8, // Hexadecimal representation of the value
|
||||
int: u31,
|
||||
|
||||
pub fn jsonParse(
|
||||
allocator: std.mem.Allocator,
|
||||
source: anytype,
|
||||
options: std.json.ParseOptions,
|
||||
) std.json.ParseError(@TypeOf(source.*))!@This() {
|
||||
_ = options;
|
||||
switch (try source.nextAlloc(allocator, .alloc_if_needed)) {
|
||||
inline .string, .allocated_string => |s| return @This(){ .bitflag = s },
|
||||
inline .number, .allocated_number => |s| return @This(){ .int = try std.fmt.parseInt(u31, s, 10) },
|
||||
else => return error.UnexpectedToken,
|
||||
}
|
||||
}
|
||||
pub const jsonStringify = @compileError("not supported");
|
||||
},
|
||||
capabilities: [][]const u8 = &[_][]const u8{},
|
||||
/// Valid for .ValueEnum and .BitEnum
|
||||
extensions: [][]const u8 = &[_][]const u8{},
|
||||
/// `quantifier` will always be `null`.
|
||||
parameters: []Operand = &[_]Operand{},
|
||||
version: ?[]const u8 = null,
|
||||
lastVersion: ?[]const u8 = null,
|
||||
};
|
||||
4
src/sysgpu/tools/validate_spirv.sh
Normal file
4
src/sysgpu/tools/validate_spirv.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
ls zig-out/spirv/ | while read -r file
|
||||
do
|
||||
spirv-val zig-out/spirv/$file
|
||||
done
|
||||
419
src/sysgpu/utils.zig
Normal file
419
src/sysgpu/utils.zig
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
const std = @import("std");
|
||||
const limits = @import("limits.zig");
|
||||
const shader = @import("shader.zig");
|
||||
const sysgpu = @import("sysgpu/main.zig");
|
||||
|
||||
pub fn Manager(comptime T: type) type {
|
||||
return struct {
|
||||
count: u32 = 1,
|
||||
|
||||
pub fn reference(manager: *@This()) void {
|
||||
_ = @atomicRmw(u32, &manager.count, .Add, 1, .Monotonic);
|
||||
}
|
||||
|
||||
pub fn release(manager: *@This()) void {
|
||||
if (@atomicRmw(u32, &manager.count, .Sub, 1, .Release) == 1) {
|
||||
@fence(.Acquire);
|
||||
const parent = @fieldParentPtr(T, "manager", manager);
|
||||
parent.deinit();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn findChained(comptime T: type, next_in_chain: ?*const sysgpu.ChainedStruct) ?*const T {
|
||||
const search = @as(*align(1) const sysgpu.ChainedStruct, @ptrCast(std.meta.fieldInfo(T, .chain).default_value.?));
|
||||
var chain = next_in_chain;
|
||||
while (chain) |c| {
|
||||
if (c.s_type == search.s_type) {
|
||||
return @as(*const T, @ptrCast(c));
|
||||
}
|
||||
chain = c.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn alignUp(x: usize, a: usize) usize {
|
||||
return (x + a - 1) / a * a;
|
||||
}
|
||||
|
||||
pub const FormatType = enum {
|
||||
float,
|
||||
unorm,
|
||||
unorm_srgb,
|
||||
snorm,
|
||||
uint,
|
||||
sint,
|
||||
depth,
|
||||
stencil,
|
||||
depth_stencil,
|
||||
};
|
||||
|
||||
pub fn vertexFormatType(format: sysgpu.VertexFormat) FormatType {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint8x2 => .uint,
|
||||
.uint8x4 => .uint,
|
||||
.sint8x2 => .sint,
|
||||
.sint8x4 => .sint,
|
||||
.unorm8x2 => .unorm,
|
||||
.unorm8x4 => .unorm,
|
||||
.snorm8x2 => .snorm,
|
||||
.snorm8x4 => .snorm,
|
||||
.uint16x2 => .uint,
|
||||
.uint16x4 => .uint,
|
||||
.sint16x2 => .sint,
|
||||
.sint16x4 => .sint,
|
||||
.unorm16x2 => .unorm,
|
||||
.unorm16x4 => .unorm,
|
||||
.snorm16x2 => .snorm,
|
||||
.snorm16x4 => .snorm,
|
||||
.float16x2 => .float,
|
||||
.float16x4 => .float,
|
||||
.float32 => .float,
|
||||
.float32x2 => .float,
|
||||
.float32x3 => .float,
|
||||
.float32x4 => .float,
|
||||
.uint32 => .uint,
|
||||
.uint32x2 => .uint,
|
||||
.uint32x3 => .uint,
|
||||
.uint32x4 => .uint,
|
||||
.sint32 => .sint,
|
||||
.sint32x2 => .sint,
|
||||
.sint32x3 => .sint,
|
||||
.sint32x4 => .sint,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn textureFormatType(format: sysgpu.Texture.Format) FormatType {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.r8_unorm => .unorm,
|
||||
.r8_snorm => .snorm,
|
||||
.r8_uint => .uint,
|
||||
.r8_sint => .sint,
|
||||
.r16_uint => .uint,
|
||||
.r16_sint => .sint,
|
||||
.r16_float => .float,
|
||||
.rg8_unorm => .unorm,
|
||||
.rg8_snorm => .snorm,
|
||||
.rg8_uint => .uint,
|
||||
.rg8_sint => .sint,
|
||||
.r32_float => .float,
|
||||
.r32_uint => .uint,
|
||||
.r32_sint => .sint,
|
||||
.rg16_uint => .uint,
|
||||
.rg16_sint => .sint,
|
||||
.rg16_float => .float,
|
||||
.rgba8_unorm => .unorm,
|
||||
.rgba8_unorm_srgb => .unorm_srgb,
|
||||
.rgba8_snorm => .snorm,
|
||||
.rgba8_uint => .uint,
|
||||
.rgba8_sint => .sint,
|
||||
.bgra8_unorm => .unorm,
|
||||
.bgra8_unorm_srgb => .unorm_srgb,
|
||||
.rgb10_a2_unorm => .unorm,
|
||||
.rg11_b10_ufloat => .float,
|
||||
.rgb9_e5_ufloat => .float,
|
||||
.rg32_float => .float,
|
||||
.rg32_uint => .uint,
|
||||
.rg32_sint => .sint,
|
||||
.rgba16_uint => .uint,
|
||||
.rgba16_sint => .sint,
|
||||
.rgba16_float => .float,
|
||||
.rgba32_float => .float,
|
||||
.rgba32_uint => .uint,
|
||||
.rgba32_sint => .sint,
|
||||
.stencil8 => .stencil,
|
||||
.depth16_unorm => .depth,
|
||||
.depth24_plus => .depth,
|
||||
.depth24_plus_stencil8 => .depth_stencil,
|
||||
.depth32_float => .depth,
|
||||
.depth32_float_stencil8 => .depth_stencil,
|
||||
.bc1_rgba_unorm => .unorm,
|
||||
.bc1_rgba_unorm_srgb => .unorm_srgb,
|
||||
.bc2_rgba_unorm => .unorm,
|
||||
.bc2_rgba_unorm_srgb => .unorm_srgb,
|
||||
.bc3_rgba_unorm => .unorm,
|
||||
.bc3_rgba_unorm_srgb => .unorm_srgb,
|
||||
.bc4_runorm => .unorm,
|
||||
.bc4_rsnorm => .snorm,
|
||||
.bc5_rg_unorm => .unorm,
|
||||
.bc5_rg_snorm => .snorm,
|
||||
.bc6_hrgb_ufloat => .float,
|
||||
.bc6_hrgb_float => .float,
|
||||
.bc7_rgba_unorm => .unorm,
|
||||
.bc7_rgba_unorm_srgb => .snorm,
|
||||
.etc2_rgb8_unorm => .unorm,
|
||||
.etc2_rgb8_unorm_srgb => .unorm_srgb,
|
||||
.etc2_rgb8_a1_unorm => .unorm_srgb,
|
||||
.etc2_rgb8_a1_unorm_srgb => .unorm,
|
||||
.etc2_rgba8_unorm => .unorm,
|
||||
.etc2_rgba8_unorm_srgb => .unorm_srgb,
|
||||
.eacr11_unorm => .unorm,
|
||||
.eacr11_snorm => .snorm,
|
||||
.eacrg11_unorm => .unorm,
|
||||
.eacrg11_snorm => .snorm,
|
||||
.astc4x4_unorm => .unorm,
|
||||
.astc4x4_unorm_srgb => .unorm_srgb,
|
||||
.astc5x4_unorm => .unorm,
|
||||
.astc5x4_unorm_srgb => .unorm_srgb,
|
||||
.astc5x5_unorm => .unorm,
|
||||
.astc5x5_unorm_srgb => .unorm_srgb,
|
||||
.astc6x5_unorm => .unorm,
|
||||
.astc6x5_unorm_srgb => .unorm_srgb,
|
||||
.astc6x6_unorm => .unorm,
|
||||
.astc6x6_unorm_srgb => .unorm_srgb,
|
||||
.astc8x5_unorm => .unorm,
|
||||
.astc8x5_unorm_srgb => .unorm_srgb,
|
||||
.astc8x6_unorm => .unorm,
|
||||
.astc8x6_unorm_srgb => .unorm_srgb,
|
||||
.astc8x8_unorm => .unorm,
|
||||
.astc8x8_unorm_srgb => .unorm_srgb,
|
||||
.astc10x5_unorm => .unorm,
|
||||
.astc10x5_unorm_srgb => .unorm_srgb,
|
||||
.astc10x6_unorm => .unorm,
|
||||
.astc10x6_unorm_srgb => .unorm_srgb,
|
||||
.astc10x8_unorm => .unorm,
|
||||
.astc10x8_unorm_srgb => .unorm_srgb,
|
||||
.astc10x10_unorm => .unorm,
|
||||
.astc10x10_unorm_srgb => .unorm_srgb,
|
||||
.astc12x10_unorm => .unorm,
|
||||
.astc12x10_unorm_srgb => .unorm_srgb,
|
||||
.astc12x12_unorm => .unorm,
|
||||
.astc12x12_unorm_srgb => .unorm_srgb,
|
||||
.r8_bg8_biplanar420_unorm => .unorm,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn formatHasDepthOrStencil(format: sysgpu.Texture.Format) bool {
|
||||
return switch (textureFormatType(format)) {
|
||||
.depth, .stencil, .depth_stencil => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn calcOrigin(dimension: sysgpu.Texture.Dimension, origin: sysgpu.Origin3D) struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
array_slice: u32,
|
||||
} {
|
||||
return .{
|
||||
.x = origin.x,
|
||||
.y = origin.y,
|
||||
.z = if (dimension == .dimension_3d) origin.z else 0,
|
||||
.array_slice = if (dimension == .dimension_3d) 0 else origin.z,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn calcExtent(dimension: sysgpu.Texture.Dimension, extent: sysgpu.Extent3D) struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
depth: u32,
|
||||
array_count: u32,
|
||||
} {
|
||||
return .{
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = if (dimension == .dimension_3d) extent.depth_or_array_layers else 1,
|
||||
.array_count = if (dimension == .dimension_3d) 0 else extent.depth_or_array_layers,
|
||||
};
|
||||
}
|
||||
|
||||
pub const DefaultPipelineLayoutDescriptor = struct {
|
||||
pub const Group = std.ArrayListUnmanaged(sysgpu.BindGroupLayout.Entry);
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
groups: std.BoundedArray(Group, limits.max_bind_groups) = .{},
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) DefaultPipelineLayoutDescriptor {
|
||||
return .{ .allocator = allocator };
|
||||
}
|
||||
|
||||
pub fn deinit(desc: *DefaultPipelineLayoutDescriptor) void {
|
||||
for (desc.groups.slice()) |*group| {
|
||||
group.deinit(desc.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addFunction(
|
||||
desc: *DefaultPipelineLayoutDescriptor,
|
||||
air: *const shader.Air,
|
||||
stage: sysgpu.ShaderStageFlags,
|
||||
entry_point: [*:0]const u8,
|
||||
) !void {
|
||||
if (air.findFunction(std.mem.span(entry_point))) |fn_inst| {
|
||||
const global_var_ref_list = air.refToList(fn_inst.global_var_refs);
|
||||
for (global_var_ref_list) |global_var_inst_idx| {
|
||||
const var_inst = air.getInst(global_var_inst_idx).@"var";
|
||||
if (var_inst.addr_space == .workgroup)
|
||||
continue;
|
||||
|
||||
const var_type = air.getInst(var_inst.type);
|
||||
const group: u32 = @intCast(air.resolveInt(var_inst.group) orelse return error.ConstExpr);
|
||||
const binding: u32 = @intCast(air.resolveInt(var_inst.binding) orelse return error.ConstExpr);
|
||||
|
||||
var entry: sysgpu.BindGroupLayout.Entry = .{ .binding = binding, .visibility = stage };
|
||||
switch (var_type) {
|
||||
.sampler_type => entry.sampler.type = .filtering,
|
||||
.comparison_sampler_type => entry.sampler.type = .comparison,
|
||||
.texture_type => |texture| {
|
||||
switch (texture.kind) {
|
||||
.storage_1d,
|
||||
.storage_2d,
|
||||
.storage_2d_array,
|
||||
.storage_3d,
|
||||
=> {
|
||||
entry.storage_texture.access = .undefined; // TODO - write_only
|
||||
entry.storage_texture.format = switch (texture.texel_format) {
|
||||
.none => unreachable,
|
||||
.rgba8unorm => .rgba8_unorm,
|
||||
.rgba8snorm => .rgba8_snorm,
|
||||
.bgra8unorm => .bgra8_unorm,
|
||||
.rgba16float => .rgba16_float,
|
||||
.r32float => .r32_float,
|
||||
.rg32float => .rg32_float,
|
||||
.rgba32float => .rgba32_float,
|
||||
.rgba8uint => .rgba8_uint,
|
||||
.rgba16uint => .rgba16_uint,
|
||||
.r32uint => .r32_uint,
|
||||
.rg32uint => .rg32_uint,
|
||||
.rgba32uint => .rgba32_uint,
|
||||
.rgba8sint => .rgba8_sint,
|
||||
.rgba16sint => .rgba16_sint,
|
||||
.r32sint => .r32_sint,
|
||||
.rg32sint => .rg32_sint,
|
||||
.rgba32sint => .rgba32_sint,
|
||||
};
|
||||
entry.storage_texture.view_dimension = switch (texture.kind) {
|
||||
.storage_1d => .dimension_1d,
|
||||
.storage_2d => .dimension_2d,
|
||||
.storage_2d_array => .dimension_2d_array,
|
||||
.storage_3d => .dimension_3d,
|
||||
else => unreachable,
|
||||
};
|
||||
},
|
||||
else => {
|
||||
// sample_type
|
||||
entry.texture.sample_type =
|
||||
switch (texture.kind) {
|
||||
.depth_2d,
|
||||
.depth_2d_array,
|
||||
.depth_cube,
|
||||
.depth_cube_array,
|
||||
=> .depth,
|
||||
else => switch (texture.texel_format) {
|
||||
.none => .float, // TODO - is this right?
|
||||
.rgba8unorm,
|
||||
.rgba8snorm,
|
||||
.bgra8unorm,
|
||||
.rgba16float,
|
||||
.r32float,
|
||||
.rg32float,
|
||||
.rgba32float,
|
||||
=> .float, // TODO - unfilterable
|
||||
.rgba8uint,
|
||||
.rgba16uint,
|
||||
.r32uint,
|
||||
.rg32uint,
|
||||
.rgba32uint,
|
||||
=> .uint,
|
||||
.rgba8sint,
|
||||
.rgba16sint,
|
||||
.r32sint,
|
||||
.rg32sint,
|
||||
.rgba32sint,
|
||||
=> .sint,
|
||||
},
|
||||
};
|
||||
entry.texture.view_dimension = switch (texture.kind) {
|
||||
.sampled_1d,
|
||||
.storage_1d,
|
||||
=> .dimension_1d,
|
||||
.sampled_2d,
|
||||
.multisampled_2d,
|
||||
.multisampled_depth_2d,
|
||||
.storage_2d,
|
||||
.depth_2d,
|
||||
=> .dimension_2d,
|
||||
.sampled_2d_array,
|
||||
.storage_2d_array,
|
||||
.depth_2d_array,
|
||||
=> .dimension_2d_array,
|
||||
.sampled_3d,
|
||||
.storage_3d,
|
||||
=> .dimension_3d,
|
||||
.sampled_cube,
|
||||
.depth_cube,
|
||||
=> .dimension_cube,
|
||||
.sampled_cube_array,
|
||||
.depth_cube_array,
|
||||
=> .dimension_cube_array,
|
||||
};
|
||||
entry.texture.multisampled = switch (texture.kind) {
|
||||
.multisampled_2d,
|
||||
.multisampled_depth_2d,
|
||||
=> .true,
|
||||
else => .false,
|
||||
};
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
switch (var_inst.addr_space) {
|
||||
.uniform => entry.buffer.type = .uniform,
|
||||
.storage => {
|
||||
if (var_inst.access_mode == .read) {
|
||||
entry.buffer.type = .read_only_storage;
|
||||
} else {
|
||||
entry.buffer.type = .storage;
|
||||
}
|
||||
},
|
||||
else => std.debug.panic("unhandled addr_space\n", .{}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
while (desc.groups.len <= group) {
|
||||
desc.groups.appendAssumeCapacity(.{});
|
||||
}
|
||||
|
||||
var append = true;
|
||||
var group_entries = &desc.groups.buffer[group];
|
||||
for (group_entries.items) |*previous_entry| {
|
||||
if (previous_entry.binding == binding) {
|
||||
// TODO - bitfield or?
|
||||
if (entry.visibility.vertex)
|
||||
previous_entry.visibility.vertex = true;
|
||||
if (entry.visibility.fragment)
|
||||
previous_entry.visibility.fragment = true;
|
||||
if (entry.visibility.compute)
|
||||
previous_entry.visibility.compute = true;
|
||||
|
||||
if (previous_entry.buffer.min_binding_size < entry.buffer.min_binding_size) {
|
||||
previous_entry.buffer.min_binding_size = entry.buffer.min_binding_size;
|
||||
}
|
||||
if (previous_entry.texture.sample_type != entry.texture.sample_type) {
|
||||
if (previous_entry.texture.sample_type == .unfilterable_float and entry.texture.sample_type == .float) {
|
||||
previous_entry.texture.sample_type = .float;
|
||||
} else if (previous_entry.texture.sample_type == .float and entry.texture.sample_type == .unfilterable_float) {
|
||||
// ignore
|
||||
} else {
|
||||
return error.IncompatibleEntries;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - any other differences return error
|
||||
|
||||
append = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (append)
|
||||
try group_entries.append(desc.allocator, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
3612
src/sysgpu/vulkan.zig
Normal file
3612
src/sysgpu/vulkan.zig
Normal file
File diff suppressed because it is too large
Load diff
511
src/sysgpu/vulkan/conv.zig
Normal file
511
src/sysgpu/vulkan/conv.zig
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
const vk = @import("vulkan");
|
||||
const sysgpu = @import("../sysgpu/main.zig");
|
||||
const vulkan = @import("../vulkan.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
|
||||
pub fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
|
||||
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
|
||||
}
|
||||
|
||||
pub fn sysgpuAdapterType(device_type: vk.PhysicalDeviceType) sysgpu.Adapter.Type {
|
||||
return switch (device_type) {
|
||||
.integrated_gpu => .integrated_gpu,
|
||||
.discrete_gpu => .discrete_gpu,
|
||||
.cpu => .cpu,
|
||||
else => .unknown,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanAccessFlagsForBufferRead(usage: sysgpu.Buffer.UsageFlags) vk.AccessFlags {
|
||||
return .{
|
||||
.indirect_command_read_bit = usage.indirect,
|
||||
.index_read_bit = usage.index,
|
||||
.vertex_attribute_read_bit = usage.vertex,
|
||||
.uniform_read_bit = usage.uniform,
|
||||
.shader_read_bit = usage.storage,
|
||||
.host_read_bit = usage.map_read,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanAccessFlagsForImageRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.AccessFlags {
|
||||
return .{
|
||||
.shader_read_bit = usage.texture_binding or usage.storage_binding,
|
||||
.color_attachment_read_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
|
||||
.depth_stencil_attachment_read_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanBlendOp(op: sysgpu.BlendOperation) vk.BlendOp {
|
||||
return switch (op) {
|
||||
.add => .add,
|
||||
.subtract => .subtract,
|
||||
.reverse_subtract => .reverse_subtract,
|
||||
.min => .min,
|
||||
.max => .max,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanBlendFactor(op: sysgpu.BlendFactor, color: bool) vk.BlendFactor {
|
||||
return switch (op) {
|
||||
.zero => .zero,
|
||||
.one => .one,
|
||||
.src => .src_color,
|
||||
.one_minus_src => .one_minus_src_color,
|
||||
.src_alpha => .src_alpha,
|
||||
.one_minus_src_alpha => .one_minus_src_alpha,
|
||||
.dst => .dst_color,
|
||||
.one_minus_dst => .one_minus_dst_color,
|
||||
.dst_alpha => .dst_alpha,
|
||||
.one_minus_dst_alpha => .one_minus_dst_alpha,
|
||||
.src_alpha_saturated => .src_alpha_saturate,
|
||||
.constant => if (color) .constant_color else .constant_alpha,
|
||||
.one_minus_constant => if (color) .one_minus_constant_color else .one_minus_constant_alpha,
|
||||
.src1 => .src1_color,
|
||||
.one_minus_src1 => .one_minus_src1_color,
|
||||
.src1_alpha => .src1_alpha,
|
||||
.one_minus_src1_alpha => .one_minus_src1_alpha,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanBufferUsageFlags(flags: sysgpu.Buffer.UsageFlags) vk.BufferUsageFlags {
|
||||
return .{
|
||||
.transfer_src_bit = flags.copy_src,
|
||||
.transfer_dst_bit = flags.copy_dst or flags.query_resolve,
|
||||
.uniform_buffer_bit = flags.uniform,
|
||||
.storage_buffer_bit = flags.storage,
|
||||
.index_buffer_bit = flags.index,
|
||||
.vertex_buffer_bit = flags.vertex,
|
||||
.indirect_buffer_bit = flags.indirect,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanCompareOp(op: sysgpu.CompareFunction) vk.CompareOp {
|
||||
return switch (op) {
|
||||
.never => .never,
|
||||
.less => .less,
|
||||
.less_equal => .less_or_equal,
|
||||
.greater => .greater,
|
||||
.greater_equal => .greater_or_equal,
|
||||
.equal => .equal,
|
||||
.not_equal => .not_equal,
|
||||
.always => .always,
|
||||
.undefined => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanCullMode(cull_mode: sysgpu.CullMode) vk.CullModeFlags {
|
||||
return .{
|
||||
.front_bit = cull_mode == .front,
|
||||
.back_bit = cull_mode == .back,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanDepthBias(ds: ?*const sysgpu.DepthStencilState) f32 {
|
||||
if (ds == null) return 0;
|
||||
return @floatFromInt(ds.?.depth_bias);
|
||||
}
|
||||
|
||||
pub fn vulkanDepthBiasClamp(ds: ?*const sysgpu.DepthStencilState) f32 {
|
||||
if (ds == null) return 0;
|
||||
return ds.?.depth_bias_clamp;
|
||||
}
|
||||
|
||||
pub fn vulkanDepthBiasSlopeScale(ds: ?*const sysgpu.DepthStencilState) f32 {
|
||||
if (ds == null) return 0;
|
||||
return ds.?.depth_bias_slope_scale;
|
||||
}
|
||||
|
||||
pub fn vulkanDescriptorType(entry: sysgpu.BindGroupLayout.Entry) vk.DescriptorType {
|
||||
switch (entry.buffer.type) {
|
||||
.undefined => {},
|
||||
|
||||
.uniform => if (entry.buffer.has_dynamic_offset == .true) {
|
||||
return .uniform_buffer_dynamic;
|
||||
} else {
|
||||
return .uniform_buffer;
|
||||
},
|
||||
|
||||
.storage,
|
||||
.read_only_storage,
|
||||
=> if (entry.buffer.has_dynamic_offset == .true) {
|
||||
return .storage_buffer_dynamic;
|
||||
} else {
|
||||
return .storage_buffer;
|
||||
},
|
||||
}
|
||||
|
||||
switch (entry.sampler.type) {
|
||||
.undefined => {},
|
||||
else => return .sampler,
|
||||
}
|
||||
|
||||
switch (entry.texture.sample_type) {
|
||||
.undefined => {},
|
||||
else => return .sampled_image,
|
||||
}
|
||||
|
||||
switch (entry.storage_texture.format) {
|
||||
.undefined => {},
|
||||
else => return .storage_image,
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn vulkanFilter(filter: sysgpu.FilterMode) vk.Filter {
|
||||
return switch (filter) {
|
||||
.nearest => .nearest,
|
||||
.linear => .linear,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanFormat(device: *const vulkan.Device, format: sysgpu.Texture.Format) vk.Format {
|
||||
return switch (format) {
|
||||
.r8_unorm => .r8_unorm,
|
||||
.r8_snorm => .r8_snorm,
|
||||
.r8_uint => .r8_uint,
|
||||
.r8_sint => .r8_sint,
|
||||
.r16_uint => .r16_uint,
|
||||
.r16_sint => .r16_sint,
|
||||
.r16_float => .r16_sfloat,
|
||||
.rg8_unorm => .r8g8_unorm,
|
||||
.rg8_snorm => .r8g8_snorm,
|
||||
.rg8_uint => .r8g8_uint,
|
||||
.rg8_sint => .r8g8_sint,
|
||||
.r32_float => .r32_sfloat,
|
||||
.r32_uint => .r32_uint,
|
||||
.r32_sint => .r32_sint,
|
||||
.rg16_uint => .r16g16_uint,
|
||||
.rg16_sint => .r16g16_sint,
|
||||
.rg16_float => .r16g16_sfloat,
|
||||
.rgba8_unorm => .r8g8b8a8_unorm,
|
||||
.rgba8_unorm_srgb => .r8g8b8a8_srgb,
|
||||
.rgba8_snorm => .r8g8b8a8_snorm,
|
||||
.rgba8_uint => .r8g8b8a8_uint,
|
||||
.rgba8_sint => .r8g8b8a8_sint,
|
||||
.bgra8_unorm => .b8g8r8a8_unorm,
|
||||
.bgra8_unorm_srgb => .b8g8r8a8_srgb,
|
||||
.rgb10_a2_unorm => .a2r10g10b10_unorm_pack32,
|
||||
.rg11_b10_ufloat => .b10g11r11_ufloat_pack32,
|
||||
.rgb9_e5_ufloat => .e5b9g9r9_ufloat_pack32,
|
||||
.rg32_float => .r32g32_sfloat,
|
||||
.rg32_uint => .r32g32_uint,
|
||||
.rg32_sint => .r32g32_sint,
|
||||
.rgba16_uint => .r16g16b16a16_uint,
|
||||
.rgba16_sint => .r16g16b16a16_sint,
|
||||
.rgba16_float => .r16g16b16a16_sfloat,
|
||||
.rgba32_float => .r32g32b32a32_sfloat,
|
||||
.rgba32_uint => .r32g32b32a32_uint,
|
||||
.rgba32_sint => .r32g32b32a32_sint,
|
||||
.stencil8 => if (device.supported_ds_formats.get(.s8_uint) != null) {
|
||||
return .s8_uint;
|
||||
} else {
|
||||
return vulkanFormat(device, .depth24_plus_stencil8);
|
||||
},
|
||||
.depth16_unorm => .d16_unorm,
|
||||
.depth24_plus => .d32_sfloat,
|
||||
.depth24_plus_stencil8 => if (device.supported_ds_formats.get(.d24_unorm_s8_uint) != null) {
|
||||
return .d24_unorm_s8_uint;
|
||||
} else {
|
||||
return .d32_sfloat_s8_uint;
|
||||
},
|
||||
.depth32_float => .d32_sfloat,
|
||||
.depth32_float_stencil8 => .d32_sfloat_s8_uint,
|
||||
.bc1_rgba_unorm => .bc1_rgba_unorm_block,
|
||||
.bc1_rgba_unorm_srgb => .bc1_rgba_srgb_block,
|
||||
.bc2_rgba_unorm => .bc2_unorm_block,
|
||||
.bc2_rgba_unorm_srgb => .bc2_srgb_block,
|
||||
.bc3_rgba_unorm => .bc3_unorm_block,
|
||||
.bc3_rgba_unorm_srgb => .bc3_srgb_block,
|
||||
.bc4_runorm => .bc4_unorm_block,
|
||||
.bc4_rsnorm => .bc4_snorm_block,
|
||||
.bc5_rg_unorm => .bc5_unorm_block,
|
||||
.bc5_rg_snorm => .bc5_snorm_block,
|
||||
.bc6_hrgb_ufloat => .bc6h_ufloat_block,
|
||||
.bc6_hrgb_float => .bc6h_sfloat_block,
|
||||
.bc7_rgba_unorm => .bc7_unorm_block,
|
||||
.bc7_rgba_unorm_srgb => .bc7_srgb_block,
|
||||
.etc2_rgb8_unorm => .etc2_r8g8b8_unorm_block,
|
||||
.etc2_rgb8_unorm_srgb => .etc2_r8g8b8_srgb_block,
|
||||
.etc2_rgb8_a1_unorm => .etc2_r8g8b8a1_unorm_block,
|
||||
.etc2_rgb8_a1_unorm_srgb => .etc2_r8g8b8a1_srgb_block,
|
||||
.etc2_rgba8_unorm => .etc2_r8g8b8a8_unorm_block,
|
||||
.etc2_rgba8_unorm_srgb => .etc2_r8g8b8a8_srgb_block,
|
||||
.eacr11_unorm => .eac_r11_unorm_block,
|
||||
.eacr11_snorm => .eac_r11_snorm_block,
|
||||
.eacrg11_unorm => .eac_r11g11_unorm_block,
|
||||
.eacrg11_snorm => .eac_r11g11_snorm_block,
|
||||
.astc4x4_unorm => .astc_4x_4_unorm_block,
|
||||
.astc4x4_unorm_srgb => .astc_4x_4_srgb_block,
|
||||
.astc5x4_unorm => .astc_5x_4_unorm_block,
|
||||
.astc5x4_unorm_srgb => .astc_5x_4_srgb_block,
|
||||
.astc5x5_unorm => .astc_5x_5_unorm_block,
|
||||
.astc5x5_unorm_srgb => .astc_5x_5_srgb_block,
|
||||
.astc6x5_unorm => .astc_6x_5_unorm_block,
|
||||
.astc6x5_unorm_srgb => .astc_6x_5_srgb_block,
|
||||
.astc6x6_unorm => .astc_6x_6_unorm_block,
|
||||
.astc6x6_unorm_srgb => .astc_6x_6_srgb_block,
|
||||
.astc8x5_unorm => .astc_8x_5_unorm_block,
|
||||
.astc8x5_unorm_srgb => .astc_8x_5_srgb_block,
|
||||
.astc8x6_unorm => .astc_8x_6_unorm_block,
|
||||
.astc8x6_unorm_srgb => .astc_8x_6_srgb_block,
|
||||
.astc8x8_unorm => .astc_8x_8_unorm_block,
|
||||
.astc8x8_unorm_srgb => .astc_8x_8_srgb_block,
|
||||
.astc10x5_unorm => .astc_1_0x_5_unorm_block,
|
||||
.astc10x5_unorm_srgb => .astc_1_0x_5_srgb_block,
|
||||
.astc10x6_unorm => .astc_1_0x_6_unorm_block,
|
||||
.astc10x6_unorm_srgb => .astc_1_0x_6_srgb_block,
|
||||
.astc10x8_unorm => .astc_1_0x_8_unorm_block,
|
||||
.astc10x8_unorm_srgb => .astc_1_0x_8_srgb_block,
|
||||
.astc10x10_unorm => .astc_1_0x_10_unorm_block,
|
||||
.astc10x10_unorm_srgb => .astc_1_0x_10_srgb_block,
|
||||
.astc12x10_unorm => .astc_1_2x_10_unorm_block,
|
||||
.astc12x10_unorm_srgb => .astc_1_2x_10_srgb_block,
|
||||
.astc12x12_unorm => .astc_1_2x_12_unorm_block,
|
||||
.astc12x12_unorm_srgb => .astc_1_2x_12_srgb_block,
|
||||
.r8_bg8_biplanar420_unorm => .g8_b8r8_2plane_420_unorm,
|
||||
.undefined => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanFrontFace(front_face: sysgpu.FrontFace) vk.FrontFace {
|
||||
return switch (front_face) {
|
||||
.ccw => vk.FrontFace.counter_clockwise,
|
||||
.cw => vk.FrontFace.clockwise,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageAspectFlags(aspect: sysgpu.Texture.Aspect, format: sysgpu.Texture.Format) vk.ImageAspectFlags {
|
||||
return switch (aspect) {
|
||||
.all => vulkanImageAspectFlagsForFormat(format),
|
||||
.stencil_only => .{ .stencil_bit = true },
|
||||
.depth_only => .{ .depth_bit = true },
|
||||
.plane0_only => .{ .plane_0_bit = true },
|
||||
.plane1_only => .{ .plane_1_bit = true },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageAspectFlagsForFormat(format: sysgpu.Texture.Format) vk.ImageAspectFlags {
|
||||
return switch (format) {
|
||||
.stencil8 => .{ .stencil_bit = true },
|
||||
.depth16_unorm, .depth24_plus, .depth32_float => .{ .depth_bit = true },
|
||||
.depth24_plus_stencil8, .depth32_float_stencil8 => .{ .depth_bit = true, .stencil_bit = true },
|
||||
.r8_bg8_biplanar420_unorm => .{ .plane_0_bit = true, .plane_1_bit = true },
|
||||
else => .{ .color_bit = true },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageCreateFlags(cube_compatible: bool, view_format_count: usize) vk.ImageCreateFlags {
|
||||
return .{
|
||||
.mutable_format_bit = view_format_count > 0,
|
||||
.cube_compatible_bit = cube_compatible,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageLayoutForRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.ImageLayout {
|
||||
// In case where we do not read, use an appropriate write state to avoid unnecessary layout changes
|
||||
return if (usage.texture_binding)
|
||||
.shader_read_only_optimal
|
||||
else if (usage.render_attachment and utils.formatHasDepthOrStencil(format))
|
||||
.depth_stencil_attachment_optimal
|
||||
else if (usage.render_attachment)
|
||||
.color_attachment_optimal
|
||||
else
|
||||
.general;
|
||||
}
|
||||
|
||||
pub fn vulkanImageLayoutForTextureBinding(sample_type: sysgpu.Texture.SampleType) vk.ImageLayout {
|
||||
return switch (sample_type) {
|
||||
.undefined => .general,
|
||||
else => .shader_read_only_optimal,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageType(dimension: sysgpu.Texture.Dimension) vk.ImageType {
|
||||
return switch (dimension) {
|
||||
.dimension_1d => .@"1d",
|
||||
.dimension_2d => .@"2d",
|
||||
.dimension_3d => .@"3d",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageUsageFlags(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.ImageUsageFlags {
|
||||
return .{
|
||||
.transfer_src_bit = usage.copy_src,
|
||||
.transfer_dst_bit = usage.copy_dst,
|
||||
.sampled_bit = usage.texture_binding,
|
||||
.storage_bit = usage.storage_binding,
|
||||
.color_attachment_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
|
||||
.transient_attachment_bit = usage.transient_attachment,
|
||||
.depth_stencil_attachment_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanImageViewType(dimension: sysgpu.TextureView.Dimension) vk.ImageViewType {
|
||||
return switch (dimension) {
|
||||
.dimension_undefined => unreachable,
|
||||
.dimension_1d => .@"1d",
|
||||
.dimension_2d => .@"2d",
|
||||
.dimension_2d_array => .@"2d_array",
|
||||
.dimension_cube => .cube,
|
||||
.dimension_cube_array => .cube_array,
|
||||
.dimension_3d => .@"3d",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanIndexType(format: sysgpu.IndexFormat) vk.IndexType {
|
||||
return switch (format) {
|
||||
.undefined => unreachable,
|
||||
.uint16 => .uint16,
|
||||
.uint32 => .uint32,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanLoadOp(op: sysgpu.LoadOp) vk.AttachmentLoadOp {
|
||||
return switch (op) {
|
||||
.load => .load,
|
||||
.clear => .clear,
|
||||
.undefined => .dont_care,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanPipelineStageFlagsForBufferRead(usage: sysgpu.Buffer.UsageFlags) vk.PipelineStageFlags {
|
||||
return .{
|
||||
.draw_indirect_bit = usage.indirect,
|
||||
.vertex_input_bit = usage.index or usage.vertex,
|
||||
.vertex_shader_bit = usage.uniform or usage.storage,
|
||||
.fragment_shader_bit = usage.uniform or usage.storage,
|
||||
.compute_shader_bit = usage.uniform or usage.storage,
|
||||
.host_bit = usage.map_read,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanPipelineStageFlagsForImageRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.PipelineStageFlags {
|
||||
return .{
|
||||
.vertex_shader_bit = usage.texture_binding or usage.storage_binding,
|
||||
.fragment_shader_bit = usage.texture_binding or usage.storage_binding,
|
||||
.early_fragment_tests_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
|
||||
.late_fragment_tests_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
|
||||
.color_attachment_output_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
|
||||
.compute_shader_bit = usage.texture_binding or usage.storage_binding,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanPrimitiveTopology(topology: sysgpu.PrimitiveTopology) vk.PrimitiveTopology {
|
||||
return switch (topology) {
|
||||
.point_list => .point_list,
|
||||
.line_list => .line_list,
|
||||
.line_strip => .line_strip,
|
||||
.triangle_list => .triangle_list,
|
||||
.triangle_strip => .triangle_strip,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanPresentMode(present_mode: sysgpu.PresentMode) vk.PresentModeKHR {
|
||||
return switch (present_mode) {
|
||||
.immediate => .immediate_khr,
|
||||
.fifo => .fifo_khr,
|
||||
.mailbox => .mailbox_khr,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanSampleCount(samples: u32) vk.SampleCountFlags {
|
||||
// TODO: https://github.com/Snektron/vulkan-zig/issues/27
|
||||
return switch (samples) {
|
||||
1 => .{ .@"1_bit" = true },
|
||||
2 => .{ .@"2_bit" = true },
|
||||
4 => .{ .@"4_bit" = true },
|
||||
8 => .{ .@"8_bit" = true },
|
||||
16 => .{ .@"16_bit" = true },
|
||||
32 => .{ .@"32_bit" = true },
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanSamplerAddressMode(address_mode: sysgpu.Sampler.AddressMode) vk.SamplerAddressMode {
|
||||
return switch (address_mode) {
|
||||
.repeat => .repeat,
|
||||
.mirror_repeat => .mirrored_repeat,
|
||||
.clamp_to_edge => .clamp_to_edge,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanSamplerMipmapMode(filter: sysgpu.MipmapFilterMode) vk.SamplerMipmapMode {
|
||||
return switch (filter) {
|
||||
.nearest => .nearest,
|
||||
.linear => .linear,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanShaderStageFlags(flags: sysgpu.ShaderStageFlags) vk.ShaderStageFlags {
|
||||
return .{
|
||||
.vertex_bit = flags.vertex,
|
||||
.fragment_bit = flags.fragment,
|
||||
.compute_bit = flags.compute,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanStencilOp(op: sysgpu.StencilOperation) vk.StencilOp {
|
||||
return switch (op) {
|
||||
.keep => .keep,
|
||||
.zero => .zero,
|
||||
.replace => .replace,
|
||||
.invert => .invert,
|
||||
.increment_clamp => .increment_and_clamp,
|
||||
.decrement_clamp => .decrement_and_clamp,
|
||||
.increment_wrap => .increment_and_wrap,
|
||||
.decrement_wrap => .decrement_and_wrap,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanStoreOp(op: sysgpu.StoreOp) vk.AttachmentStoreOp {
|
||||
return switch (op) {
|
||||
.store => .store,
|
||||
.discard => .dont_care,
|
||||
.undefined => .dont_care,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanVertexFormat(format: sysgpu.VertexFormat) vk.Format {
|
||||
return switch (format) {
|
||||
.uint8x2 => .r8g8_uint,
|
||||
.uint8x4 => .r8g8b8a8_uint,
|
||||
.sint8x2 => .r8g8_sint,
|
||||
.sint8x4 => .r8g8b8a8_sint,
|
||||
.unorm8x2 => .r8g8_unorm,
|
||||
.unorm8x4 => .r8g8b8a8_unorm,
|
||||
.snorm8x2 => .r8g8_snorm,
|
||||
.snorm8x4 => .r8g8b8a8_snorm,
|
||||
.uint16x2 => .r16g16_uint,
|
||||
.uint16x4 => .r16g16b16a16_uint,
|
||||
.sint16x2 => .r16g16_sint,
|
||||
.sint16x4 => .r16g16b16a16_sint,
|
||||
.unorm16x2 => .r16g16_unorm,
|
||||
.unorm16x4 => .r16g16b16a16_unorm,
|
||||
.snorm16x2 => .r16g16_snorm,
|
||||
.snorm16x4 => .r16g16b16a16_snorm,
|
||||
.float16x2 => .r16g16_sfloat,
|
||||
.float16x4 => .r16g16b16a16_sfloat,
|
||||
.float32 => .r32_sfloat,
|
||||
.float32x2 => .r32g32_sfloat,
|
||||
.float32x3 => .r32g32b32_sfloat,
|
||||
.float32x4 => .r32g32b32a32_sfloat,
|
||||
.uint32 => .r32_uint,
|
||||
.uint32x2 => .r32g32_uint,
|
||||
.uint32x3 => .r32g32b32_uint,
|
||||
.uint32x4 => .r32g32b32a32_uint,
|
||||
.sint32 => .r32_sint,
|
||||
.sint32x2 => .r32g32_sint,
|
||||
.sint32x3 => .r32g32b32_sint,
|
||||
.sint32x4 => .r32g32b32a32_sint,
|
||||
.undefined => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vulkanVertexInputRate(step_mode: sysgpu.VertexStepMode) vk.VertexInputRate {
|
||||
return switch (step_mode) {
|
||||
.vertex => .vertex,
|
||||
.instance => .instance,
|
||||
.vertex_buffer_not_used => unreachable,
|
||||
};
|
||||
}
|
||||
122
src/sysgpu/vulkan/proc.zig
Normal file
122
src/sysgpu/vulkan/proc.zig
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
pub const BaseFunctions = vk.BaseWrapper(.{
|
||||
.createInstance = true,
|
||||
.enumerateInstanceExtensionProperties = true,
|
||||
.enumerateInstanceLayerProperties = true,
|
||||
.getInstanceProcAddr = true,
|
||||
});
|
||||
|
||||
pub const InstanceFunctions = vk.InstanceWrapper(.{
|
||||
.createDevice = true,
|
||||
// TODO: renderdoc will not work with wayland
|
||||
// .createWaylandSurfaceKHR = builtin.target.os.tag == .linux,
|
||||
.createWin32SurfaceKHR = builtin.target.os.tag == .windows,
|
||||
.createXlibSurfaceKHR = builtin.target.os.tag == .linux,
|
||||
.destroyInstance = true,
|
||||
.destroySurfaceKHR = true,
|
||||
.enumerateDeviceExtensionProperties = true,
|
||||
.enumerateDeviceLayerProperties = true,
|
||||
.enumeratePhysicalDevices = true,
|
||||
.getDeviceProcAddr = true,
|
||||
.getPhysicalDeviceFeatures = true,
|
||||
.getPhysicalDeviceFormatProperties = true,
|
||||
.getPhysicalDeviceProperties = true,
|
||||
.getPhysicalDeviceMemoryProperties = true,
|
||||
.getPhysicalDeviceQueueFamilyProperties = true,
|
||||
.getPhysicalDeviceSurfaceCapabilitiesKHR = true,
|
||||
.getPhysicalDeviceSurfaceFormatsKHR = true,
|
||||
});
|
||||
|
||||
pub const DeviceFunctions = vk.DeviceWrapper(.{
|
||||
.acquireNextImageKHR = true,
|
||||
.allocateCommandBuffers = true,
|
||||
.allocateDescriptorSets = true,
|
||||
.allocateMemory = true,
|
||||
.beginCommandBuffer = true,
|
||||
.bindBufferMemory = true,
|
||||
.bindImageMemory = true,
|
||||
.cmdBeginRenderPass = true,
|
||||
.cmdBindDescriptorSets = true,
|
||||
.cmdBindIndexBuffer = true,
|
||||
.cmdBindPipeline = true,
|
||||
.cmdBindVertexBuffers = true,
|
||||
.cmdCopyBuffer = true,
|
||||
.cmdCopyBufferToImage = true,
|
||||
.cmdCopyImage = true,
|
||||
.cmdDispatch = true,
|
||||
.cmdDraw = true,
|
||||
.cmdDrawIndexed = true,
|
||||
.cmdEndRenderPass = true,
|
||||
.cmdPipelineBarrier = true,
|
||||
.cmdSetScissor = true,
|
||||
.cmdSetStencilReference = true,
|
||||
.cmdSetViewport = true,
|
||||
.createBuffer = true,
|
||||
.createCommandPool = true,
|
||||
.createComputePipelines = true,
|
||||
.createDescriptorPool = true,
|
||||
.createDescriptorSetLayout = true,
|
||||
.createFence = true,
|
||||
.createFramebuffer = true,
|
||||
.createGraphicsPipelines = true,
|
||||
.createImage = true,
|
||||
.createImageView = true,
|
||||
.createPipelineLayout = true,
|
||||
.createRenderPass = true,
|
||||
.createSampler = true,
|
||||
.createSemaphore = true,
|
||||
.createShaderModule = true,
|
||||
.createSwapchainKHR = true,
|
||||
.destroyBuffer = true,
|
||||
.destroyCommandPool = true,
|
||||
.destroyDescriptorPool = true,
|
||||
.destroyDescriptorSetLayout = true,
|
||||
.destroyDevice = true,
|
||||
.destroyFence = true,
|
||||
.destroyFramebuffer = true,
|
||||
.destroyImage = true,
|
||||
.destroyImageView = true,
|
||||
.destroyPipeline = true,
|
||||
.destroyPipelineLayout = true,
|
||||
.destroyRenderPass = true,
|
||||
.destroySampler = true,
|
||||
.destroySemaphore = true,
|
||||
.destroyShaderModule = true,
|
||||
.destroySwapchainKHR = true,
|
||||
.deviceWaitIdle = true,
|
||||
.endCommandBuffer = true,
|
||||
.freeCommandBuffers = true,
|
||||
.freeDescriptorSets = true,
|
||||
.freeMemory = true,
|
||||
.getBufferMemoryRequirements = true,
|
||||
.getDeviceQueue = true,
|
||||
.getFenceStatus = true,
|
||||
.getImageMemoryRequirements = true,
|
||||
.getSwapchainImagesKHR = true,
|
||||
.mapMemory = true,
|
||||
.queuePresentKHR = true,
|
||||
.queueSubmit = true,
|
||||
.queueWaitIdle = true,
|
||||
.resetCommandBuffer = true,
|
||||
.resetFences = true,
|
||||
.unmapMemory = true,
|
||||
.updateDescriptorSets = true,
|
||||
.waitForFences = true,
|
||||
});
|
||||
|
||||
pub const BaseLoader = *const fn (vk.Instance, [*:0]const u8) vk.PfnVoidFunction;
|
||||
|
||||
pub fn loadBase(baseLoader: BaseLoader) !BaseFunctions {
|
||||
return BaseFunctions.load(baseLoader) catch return error.ProcLoadingFailed;
|
||||
}
|
||||
|
||||
pub fn loadInstance(instance: vk.Instance, instanceLoader: vk.PfnGetInstanceProcAddr) !InstanceFunctions {
|
||||
return InstanceFunctions.load(instance, instanceLoader) catch return error.ProcLoadingFailed;
|
||||
}
|
||||
|
||||
pub fn loadDevice(device: vk.Device, deviceLoader: vk.PfnGetDeviceProcAddr) !DeviceFunctions {
|
||||
return DeviceFunctions.load(device, deviceLoader) catch return error.ProcLoadingFailed;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue