Last active
September 14, 2020 21:28
Star
You must be signed in to star a gist
Direct3D12 Sample in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const win32 = @import("win32.zig"); | |
const d3d12 = @import("d3d12.zig"); | |
const dxgi = @import("dxgi1_4.zig"); | |
const d3dcompiler = @import("d3dcompiler.zig"); | |
const windows = std.os.windows; | |
const L = std.unicode.utf8ToUtf16LeStringLiteral; | |
const WindowX = 100; | |
const WindowY = 100; | |
const WindowWidth = 1280; | |
const WindowHeight = 720; | |
const Fullscreen = false; | |
//BEGIN D3D12 variables | |
const FrameBufferCount = 3; | |
var device: ?*d3d12.ID3D12Device = null; | |
var swap_chain: ?*dxgi.IDXGISwapChain3 = null; | |
var command_queue: ?*d3d12.ID3D12CommandQueue = null; | |
var rtv_descriptor_heap: ?*d3d12.ID3D12DescriptorHeap = null; | |
var render_targets: [FrameBufferCount]?*d3d12.ID3D12Resource = undefined; | |
var command_allocator: [FrameBufferCount]?*d3d12.ID3D12CommandAllocator = undefined; | |
var command_list: ?*d3d12.ID3D12GraphicsCommandList = null; | |
var fence: [FrameBufferCount]?*d3d12.ID3D12Fence = undefined; | |
var fence_event: win32.HANDLE = null; | |
var fence_value: [FrameBufferCount]u64 = undefined; | |
var frame_index: usize = 0; | |
var rtv_descriptor_size: usize = 0; | |
var pipeline_state_object: ?*d3d12.ID3D12PipelineState = null; | |
var root_signature: ?*d3d12.ID3D12RootSignature = null; | |
var viewport: d3d12.D3D12_VIEWPORT = undefined; | |
var scissor_rect: d3d12.D3D12_RECT = undefined; | |
var vertex_buffer: ?*d3d12.ID3D12Resource = null; | |
var vertex_buffer_view: d3d12.D3D12_VERTEX_BUFFER_VIEW = undefined; | |
var index_buffer: ?*d3d12.ID3D12Resource = null; | |
var index_buffer_view: d3d12.D3D12_INDEX_BUFFER_VIEW = undefined; | |
var depth_stencil_buffer: ?*d3d12.ID3D12Resource = null; | |
var depth_descriptor_heap: ?*d3d12.ID3D12DescriptorHeap = null; | |
var main_descriptor_heap: [FrameBufferCount]?*d3d12.ID3D12DescriptorHeap = undefined; | |
var constant_buffer_upload_heap: [FrameBufferCount]?*d3d12.ID3D12Resource = undefined; | |
var color_multiplier_data: ConstantBuffer = undefined; | |
var color_multiplier_gpu_address: [FrameBufferCount]*u8 = undefined; | |
//END | |
var hwnd: win32.HWND = undefined; | |
// TODO: use Color from zigimg | |
pub const Color = struct { | |
R: f32, | |
G: f32, | |
B: f32, | |
A: f32, | |
const Self = @This(); | |
pub fn initRGB(r: f32, g: f32, b: f32) Self { | |
return Self{ | |
.R = r, | |
.G = g, | |
.B = b, | |
.A = 1.0, | |
}; | |
} | |
pub fn initRGBA(r: f32, g: f32, b: f32, a: f32) Self { | |
return Self{ | |
.R = r, | |
.G = g, | |
.B = b, | |
.A = a, | |
}; | |
} | |
pub fn premultipliedAlpha(self: Self) Self { | |
return Self{ | |
.R = self.R * self.A, | |
.G = self.G * self.A, | |
.B = self.B * self.A, | |
.A = self.A, | |
}; | |
} | |
}; | |
const Float3 = struct { | |
x: f32 = 0.0, | |
y: f32 = 0.0, | |
z: f32 = 0.0, | |
}; | |
const Vertex = struct { | |
position: Float3, | |
color: Color, | |
}; | |
const ConstantBuffer = struct { | |
color_multiplier: Color | |
}; | |
const input_layout = [_]d3d12.D3D12_INPUT_ELEMENT_DESC{ | |
.{ | |
.SemanticName = "POSITION", | |
.SemanticIndex = 0, | |
.Format = ._R32G32B32_FLOAT, | |
.InputSlot = 0, | |
.AlignedByteOffset = 0, | |
.InputSlotClass = ._PER_VERTEX_DATA, | |
.InstanceDataStepRate = 0, | |
}, | |
.{ | |
.SemanticName = "COLOR", | |
.SemanticIndex = 0, | |
.Format = ._R32G32B32A32_FLOAT, | |
.InputSlot = 0, | |
.AlignedByteOffset = @byteOffsetOf(Vertex, "color"), | |
.InputSlotClass = ._PER_VERTEX_DATA, | |
.InstanceDataStepRate = 0, | |
}, | |
}; | |
const quad_vertices = [_]Vertex{ | |
// First Quad (closer to camera, blue) | |
// top left | |
.{ | |
.position = .{ .x = -0.5, .y = 0.5, .z = 0.5 }, | |
.color = Color.initRGB(0.0, 0.0, 1.0), | |
}, | |
// bottom right | |
.{ | |
.position = .{ .x = 0.5, .y = -0.5, .z = 0.5 }, | |
.color = Color.initRGB(0.0, 0.0, 1.0), | |
}, | |
// bottom left | |
.{ | |
.position = .{ .x = -0.5, .y = -0.5, .z = 0.5 }, | |
.color = Color.initRGB(0.0, 0.0, 1.0), | |
}, | |
// top right | |
.{ | |
.position = .{ .x = 0.5, .y = 0.5, .z = 0.5 }, | |
.color = Color.initRGB(0.0, 0.0, 1.0), | |
}, | |
// // Second Quad (closer to camera, blue) | |
// // top left | |
// .{ | |
// .position = .{ .x = -0.75, .y = 0.75, .z = 0.7 }, | |
// .color = Color.initRGB(0.0, 1.0, 0.0), | |
// }, | |
// // bottom right | |
// .{ | |
// .position = .{ .x = 0.0, .y = 0.0, .z = 0.7 }, | |
// .color = Color.initRGB(0.0, 1.0, 0.0), | |
// }, | |
// // bottom left | |
// .{ | |
// .position = .{ .x = -0.75, .y = 0.0, .z = 0.7 }, | |
// .color = Color.initRGB(0.0, 1.0, 0.0), | |
// }, | |
// // top right | |
// .{ | |
// .position = .{ .x = 0.0, .y = 0.75, .z = 0.7 }, | |
// .color = Color.initRGB(0.0, 1.0, 0.0), | |
// }, | |
}; | |
const quad_indices = [_]u32{ | |
0, 1, 2, // First triangle | |
0, 3, 1, // Second triangle | |
}; | |
fn bufferSize(buffer: anytype) usize { | |
return std.mem.len(buffer) * @sizeOf(std.meta.Child(@TypeOf(buffer))); | |
} | |
fn comFindReturnType(comptime vtable_type: type, comptime name: []const u8) type { | |
@setEvalBranchQuota(4000); | |
for (@typeInfo(vtable_type).Struct.fields) |field| { | |
if (std.mem.eql(u8, field.name, name)) { | |
const func_type = @typeInfo(@typeInfo(field.field_type).Optional.child); | |
return func_type.Fn.return_type.?; | |
} | |
} | |
return void; | |
} | |
fn comFindVtableType(comptime parent_type: type) type { | |
switch (@typeInfo(parent_type)) { | |
.Struct => |struct_info| { | |
for (struct_info.fields) |field| { | |
if (std.mem.eql(u8, field.name, "lpVtbl")) { | |
return @typeInfo(field.field_type).Pointer.child; | |
} | |
} | |
}, | |
.Pointer => |pointer_info| { | |
for (@typeInfo(pointer_info.child).Struct.fields) |field| { | |
if (std.mem.eql(u8, field.name, "lpVtbl")) { | |
return @typeInfo(field.field_type).Pointer.child; | |
} | |
} | |
}, | |
.Optional => |optional| { | |
const pointer_type = std.meta.Child(optional.child); | |
for (@typeInfo(pointer_type).Struct.fields) |field| { | |
if (std.mem.eql(u8, field.name, "lpVtbl")) { | |
return @typeInfo(field.field_type).Pointer.child; | |
} | |
} | |
}, | |
else => { | |
@compileError("Type not handled in comFindVTableType"); | |
}, | |
} | |
return void; | |
} | |
fn comCall(self: anytype, comptime name: []const u8, args: anytype) comFindReturnType(comFindVtableType(@TypeOf(self)), name) { | |
if (@typeInfo(@TypeOf(self)) == .Optional) { | |
if (self) |self_ptr| { | |
if (@field(self_ptr.lpVtbl[0], name)) |func| { | |
return @call(.{}, func, .{self_ptr} ++ args); | |
} | |
} | |
} else { | |
if (@field(self.lpVtbl[0], name)) |func| { | |
return @call(.{}, func, .{self} ++ args); | |
} | |
} | |
return undefined; | |
} | |
inline fn comCast(comptime target: type, instance: anytype) [*c]target { | |
return @ptrCast([*c]target, instance); | |
} | |
inline fn comRelease(instance: anytype) void { | |
if (@typeInfo(@TypeOf(instance)) == .Optional) { | |
if (instance) |instance_ptr| { | |
_ = comCall(instance_ptr, "Release", .{}); | |
} | |
} else { | |
_ = comCall(instance, "Release", .{}); | |
} | |
} | |
fn getComInteface(self: anytype, interface: anytype, result: anytype) !void { | |
const hr = comCall(self, "QueryInterface", .{ interface, @ptrCast([*c]?*c_void, result) }); | |
if (hr < 0) { | |
return error.QueryInterfaceFailed; | |
} | |
} | |
inline fn uuidof(comptime parent_type: type, comptime interface: type) [*c]const @field(parent_type, "IID") { | |
const interface_full_name = @typeName(interface); | |
const index = comptime std.mem.lastIndexOf(u8, interface_full_name, "_").? + 1; | |
const iid_name = "IID_" ++ interface_full_name[index..]; | |
return &@field(parent_type, iid_name); | |
} | |
inline fn createDXFactoryObject(instance: anytype) win32.HRESULT { | |
const instance_type = std.meta.Child(std.meta.Child(std.meta.Child(@TypeOf(instance)))); | |
return dxgi.CreateDXGIFactory2(0, uuidof(dxgi, instance_type), @ptrCast([*c]?*c_void, instance)); | |
} | |
inline fn memcpySubresource( | |
dest: *const d3d12.D3D12_MEMCPY_DEST, | |
source: *const d3d12.D3D12_SUBRESOURCE_DATA, | |
row_size_in_bytes: usize, | |
num_rows: u32, | |
num_slices: u32, | |
) void { | |
var slice: usize = 0; | |
while (slice < num_slices) : (slice += 1) { | |
const dest_slice = @intToPtr([*]u8, @ptrToInt(dest.pData) + @intCast(usize, dest.SlicePitch) * slice); | |
const source_slice = @intToPtr([*]const u8, @ptrToInt(source.pData) + @intCast(usize, source.SlicePitch) * slice); | |
var row: usize = 0; | |
while (row < num_rows) : (row += 1) { | |
const dest_row = @intToPtr([*]u8, @ptrToInt(dest_slice) + @intCast(usize, dest.RowPitch) * row); | |
const source_row = @intToPtr([*]const u8, @ptrToInt(source_slice) + @intCast(usize, source.RowPitch) * row); | |
std.mem.copy(u8, dest_row[0..row_size_in_bytes], source_row[0..row_size_in_bytes]); | |
} | |
} | |
} | |
fn updateSubresourcesAlloc( | |
allocator: *std.mem.Allocator, | |
commands: ?*d3d12.ID3D12GraphicsCommandList, | |
destination: ?*d3d12.ID3D12Resource, | |
intermediate: ?*d3d12.ID3D12Resource, | |
intermediate_offset: usize, | |
first_subresource: usize, | |
num_subresources: usize, | |
src_data: [*]d3d12.D3D12_SUBRESOURCE_DATA, | |
) !u64 { | |
const memory_to_alloc = (@sizeOf(d3d12.D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + @sizeOf(u32) + @sizeOf(u64)) * num_subresources; | |
const memory = try allocator.alloc(u8, memory_to_alloc); | |
defer allocator.free(memory); | |
const layouts = @intToPtr([*]d3d12.D3D12_PLACED_SUBRESOURCE_FOOTPRINT, @ptrToInt(&memory[0])); | |
const row_sizes_in_bytes = @intToPtr([*]u64, @ptrToInt(layouts) + num_subresources * @sizeOf(@TypeOf(layouts[0]))); | |
const num_rows = @intToPtr([*]u32, @ptrToInt(row_sizes_in_bytes) + num_subresources * @sizeOf(*u64)); | |
var desc: d3d12.D3D12_RESOURCE_DESC = undefined; | |
_ = comCall(destination, "GetDesc", .{&desc}); | |
var dest_device: ?*d3d12.ID3D12Device = null; | |
_ = comCall(destination, "GetDevice", .{ uuidof(d3d12, d3d12.ID3D12Device), @ptrCast([*c]?*c_void, &dest_device) }); | |
defer comRelease(dest_device); | |
var required_size: u64 = 0; | |
comCall(dest_device, "GetCopyableFootprints", .{ &desc, @intCast(c_uint, first_subresource), @intCast(c_uint, num_subresources), @intCast(c_uint, intermediate_offset), layouts, num_rows, row_sizes_in_bytes, &required_size }); | |
const result: u64 = try updateSubresources(commands, destination, intermediate, intermediate_offset, first_subresource, num_subresources, required_size, layouts, num_rows, row_sizes_in_bytes, src_data); | |
return result; | |
} | |
fn updateSubresources( | |
commands: ?*d3d12.ID3D12GraphicsCommandList, | |
destination: ?*d3d12.ID3D12Resource, | |
intermediate: ?*d3d12.ID3D12Resource, | |
intermediate_offset: usize, | |
first_subresource: usize, | |
num_subresources: usize, | |
required_size: u64, | |
layouts: [*]d3d12.D3D12_PLACED_SUBRESOURCE_FOOTPRINT, | |
num_rows: [*]u32, | |
row_sizes_in_bytes: [*]u64, | |
src_data: [*]d3d12.D3D12_SUBRESOURCE_DATA, | |
) !u64 { | |
var intermediate_desc: d3d12.D3D12_RESOURCE_DESC = undefined; | |
comCall(intermediate, "GetDesc", .{&intermediate_desc}); | |
var destination_desc: d3d12.D3D12_RESOURCE_DESC = undefined; | |
comCall(destination, "GetDesc", .{&destination_desc}); | |
if (intermediate_desc.Dimension != ._BUFFER or intermediate_desc.Width < (required_size + layouts[0].Offset) or required_size > (std.math.maxInt(usize) - 1) or (destination_desc.Dimension == ._BUFFER and (first_subresource != 0 or num_subresources != 1))) { | |
return error.InvalidIntermediateAndDestination; | |
} | |
var data: ?*u8 = null; | |
var hr = comCall(intermediate, "Map", .{ 0, null, @ptrCast([*c]?*c_void, &data) }); | |
if (hr < 0) { | |
return error.MapFailed; | |
} | |
defer comCall(intermediate, "Unmap", .{ 0, null }); | |
var index: usize = 0; | |
while (index < num_subresources) : (index += 1) { | |
if (row_sizes_in_bytes[index] > (std.math.maxInt(u64) - 1)) { | |
return error.RowSizeTooLarge; | |
} | |
var dest_data: d3d12.D3D12_MEMCPY_DEST = .{ | |
.pData = @intToPtr(*c_void, @ptrToInt(data.?) + layouts[index].Offset), | |
.RowPitch = layouts[index].Footprint.RowPitch, | |
.SlicePitch = layouts[index].Footprint.RowPitch * num_rows[index], | |
}; | |
memcpySubresource(&dest_data, &src_data[index], row_sizes_in_bytes[index], num_rows[index], layouts[index].Footprint.Depth); | |
} | |
if (destination_desc.Dimension == ._BUFFER) { | |
comCall(commands, "CopyBufferRegion", .{ destination, 0, intermediate, layouts[0].Offset, layouts[0].Footprint.Width }); | |
} else { | |
index = 0; | |
while (index < num_subresources) : (index += 1) { | |
var dest_texture: d3d12.D3D12_TEXTURE_COPY_LOCATION = .{ | |
.pResource = destination, | |
.Type = ._SUBRESOURCE_INDEX, | |
.unnamed_0 = .{ | |
.SubresourceIndex = @intCast(c_uint, first_subresource + index), | |
}, | |
}; | |
var source_texture: d3d12.D3D12_TEXTURE_COPY_LOCATION = .{ | |
.pResource = intermediate, | |
.Type = ._PLACED_FOOTPRINT, | |
.unnamed_0 = .{ | |
.PlacedFootprint = layouts[index], | |
}, | |
}; | |
comCall(commands, "CopyTextureRegion", .{ &dest_texture, 0, 0, 0, &source_texture, null }); | |
} | |
} | |
return required_size; | |
} | |
fn initDirect3D() !void { | |
var hr: win32.HRESULT = undefined; | |
if (std.builtin.mode == .Debug) { | |
var debug_control: ?*d3d12.ID3D12Debug1 = null; | |
hr = d3d12.D3D12GetDebugInterface(uuidof(d3d12, d3d12.ID3D12Debug1), @ptrCast([*c]?*c_void, &debug_control)); | |
if (hr >= 0) { | |
comCall(debug_control, "EnableDebugLayer", .{}); | |
_ = comCall(debug_control, "Release", .{}); | |
} | |
} | |
// Create DXGI factory | |
var dxgi_factory: ?*dxgi.IDXGIFactory4 = null; | |
hr = createDXFactoryObject(&dxgi_factory); | |
if (hr < 0) { | |
return error.DXGIFactoryCreationFailed; | |
} | |
// Find proper adapter for D3D12 device | |
var adapter: ?*dxgi.IDXGIAdapter1 = null; | |
var adapter_index: u32 = 0; | |
var adapter_found = false; | |
while (comCall(dxgi_factory, "EnumAdapters1", .{ adapter_index, &adapter }) != 0x887A0002) { | |
var desc: dxgi.DXGI_ADAPTER_DESC1 = undefined; | |
_ = comCall(adapter, "GetDesc1", .{&desc}); | |
if ((desc.Flags & @as(u32, dxgi.DXGI_ADAPTER_FLAG_SOFTWARE)) == dxgi.DXGI_ADAPTER_FLAG_SOFTWARE) { | |
adapter_index += 1; | |
continue; | |
} | |
hr = d3d12.D3D12CreateDevice(comCast(d3d12.IUnknown, adapter.?), ._11_0, uuidof(d3d12, d3d12.ID3D12Device), null); | |
if (hr >= 0) { | |
adapter_found = true; | |
break; | |
} | |
adapter_index += 1; | |
} | |
// Create the actual device | |
hr = d3d12.D3D12CreateDevice(comCast(d3d12.IUnknown, adapter.?), ._11_0, uuidof(d3d12, d3d12.ID3D12Device), @ptrCast([*c]?*c_void, &device)); | |
if (hr < 0) { | |
return error.DeviceCreationFailed; | |
} | |
// Create a direct command queue | |
var command_queue_desc = std.mem.zeroes(d3d12.D3D12_COMMAND_QUEUE_DESC); | |
command_queue_desc.Flags = .D3D12_COMMAND_QUEUE_FLAG_NONE; | |
command_queue_desc.Type = ._DIRECT; | |
hr = comCall(device, "CreateCommandQueue", .{ &command_queue_desc, uuidof(d3d12, d3d12.ID3D12CommandQueue), @ptrCast([*c]?*c_void, &command_queue) }); | |
if (hr < 0) { | |
return error.CommandQueueCreationFailed; | |
} | |
// Create the swap chain | |
var swap_chain_desc: dxgi.DXGI_SWAP_CHAIN_DESC1 = std.mem.zeroes(dxgi.DXGI_SWAP_CHAIN_DESC1); | |
swap_chain_desc.Format = ._R8G8B8A8_UNORM; | |
swap_chain_desc.SampleDesc.Count = 1; | |
swap_chain_desc.BufferUsage = dxgi.DXGI_USAGE_RENDER_TARGET_OUTPUT; | |
swap_chain_desc.BufferCount = FrameBufferCount; | |
swap_chain_desc.Scaling = ._NONE; | |
swap_chain_desc.SwapEffect = ._FLIP_SEQUENTIAL; | |
var fullscreen_desc: dxgi.DXGI_SWAP_CHAIN_FULLSCREEN_DESC = std.mem.zeroes(dxgi.DXGI_SWAP_CHAIN_FULLSCREEN_DESC); | |
fullscreen_desc.RefreshRate.Numerator = 60; | |
fullscreen_desc.RefreshRate.Denominator = 1; | |
fullscreen_desc.Windowed = @boolToInt(!Fullscreen); | |
var temp_swap_chain: ?*dxgi.IDXGISwapChain1 = null; | |
hr = comCall(dxgi_factory, "CreateSwapChainForHwnd", .{ comCast(dxgi.IUnknown, command_queue.?), @ptrCast(dxgi.HWND, hwnd), &swap_chain_desc, &fullscreen_desc, null, &temp_swap_chain }); | |
if (hr < 0) { | |
return error.CreateTempSwapChainFailed; | |
} | |
defer comRelease(temp_swap_chain); | |
try getComInteface(temp_swap_chain, uuidof(dxgi, dxgi.IDXGISwapChain3), &swap_chain); | |
// Create the render target descriptor heap | |
var rtv_descriptor_desc: d3d12.D3D12_DESCRIPTOR_HEAP_DESC = std.mem.zeroes(d3d12.D3D12_DESCRIPTOR_HEAP_DESC); | |
rtv_descriptor_desc.Type = ._RTV; | |
rtv_descriptor_desc.NumDescriptors = FrameBufferCount; | |
hr = comCall(device, "CreateDescriptorHeap", .{ &rtv_descriptor_desc, uuidof(d3d12, d3d12.ID3D12DescriptorHeap), @ptrCast([*c]?*c_void, &rtv_descriptor_heap) }); | |
if (hr < 0) { | |
return error.RTVDescriptorHeapCreationFailed; | |
} | |
// Get the size of a descriptor in this heap | |
rtv_descriptor_size = @as(usize, comCall(device, "GetDescriptorHandleIncrementSize", .{d3d12.D3D12_DESCRIPTOR_HEAP_TYPE._RTV})); | |
// Get handle to the first descritor in the heap. | |
var rtv_handle: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE = undefined; | |
comCall(rtv_descriptor_heap, "GetCPUDescriptorHandleForHeapStart", .{&rtv_handle}); | |
// Create a render target view for each buffer | |
var index: usize = 0; | |
while (index < FrameBufferCount) : (index += 1) { | |
hr = comCall(swap_chain, "GetBuffer", .{ @truncate(c_uint, index), @ptrCast([*c]const dxgi.IID, uuidof(d3d12, d3d12.ID3D12Resource)), @ptrCast([*c]?*c_void, &render_targets[index]) }); | |
if (hr < 0) { | |
return error.CannotGetSwapChainBuffer; | |
} | |
comCall(device, "CreateRenderTargetView", .{ render_targets[index], null, rtv_handle }); | |
rtv_handle.ptr += rtv_descriptor_size; | |
} | |
// Create the command allocators | |
index = 0; | |
while (index < FrameBufferCount) : (index += 1) { | |
hr = comCall(device, "CreateCommandAllocator", .{ ._DIRECT, uuidof(d3d12, d3d12.ID3D12CommandAllocator), @ptrCast([*c]?*c_void, &command_allocator[index]) }); | |
if (hr < 0) { | |
return error.CommandAllocatorCreationFailed; | |
} | |
} | |
// Create the command list with the first allocator | |
hr = comCall(device, "CreateCommandList", .{ 0, ._DIRECT, command_allocator[0], null, uuidof(d3d12, d3d12.ID3D12GraphicsCommandList), @ptrCast([*]?*c_void, &command_list) }); | |
if (hr < 0) { | |
return error.CommandListCreationFailed; | |
} | |
// Create a fence and fence event | |
index = 0; | |
while (index < FrameBufferCount) : (index += 1) { | |
hr = comCall(device, "CreateFence", .{ 0, .D3D12_FENCE_FLAG_NONE, uuidof(d3d12, d3d12.ID3D12Fence), @ptrCast([*c]?*c_void, &fence[index]) }); | |
if (hr < 0) { | |
return error.CreateFenceFailed; | |
} | |
fence_value[index] = 0; | |
} | |
// Create a handle to a fence event | |
fence_event = d3d12.CreateEvent(null, @boolToInt(false), @boolToInt(false), null); | |
if (fence_event == null) { | |
return error.FenceEventCreationFailed; | |
} | |
// Create root signature | |
var descriptor_table_ranges: [1]d3d12.D3D12_DESCRIPTOR_RANGE = undefined; | |
descriptor_table_ranges[0].RangeType = ._CBV; | |
descriptor_table_ranges[0].NumDescriptors = 1; | |
descriptor_table_ranges[0].BaseShaderRegister = 0; | |
descriptor_table_ranges[0].RegisterSpace = 0; | |
descriptor_table_ranges[0].OffsetInDescriptorsFromTableStart = d3d12.D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; | |
var descriptor_table: d3d12.D3D12_ROOT_DESCRIPTOR_TABLE = std.mem.zeroes(d3d12.D3D12_ROOT_DESCRIPTOR_TABLE); | |
descriptor_table.NumDescriptorRanges = descriptor_table_ranges.len; | |
descriptor_table.pDescriptorRanges = &descriptor_table_ranges[0]; | |
// TODO: Use https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12serializeversionedrootsignature instead | |
var root_signature_desc: d3d12.D3D12_ROOT_SIGNATURE_DESC = std.mem.zeroes(d3d12.D3D12_ROOT_SIGNATURE_DESC); | |
root_signature_desc.Flags = .D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; | |
var signature: ?*d3d12.ID3DBlob = null; | |
hr = d3d12.D3D12SerializeRootSignature(&root_signature_desc, ._1, &signature, null); | |
if (hr < 0) { | |
return error.CannotSerializeRootSignature; | |
} | |
hr = comCall(device, "CreateRootSignature", .{ 0, comCall(signature, "GetBufferPointer", .{}), comCall(signature, "GetBufferSize", .{}), uuidof(d3d12, d3d12.ID3D12RootSignature), @ptrCast([*c]?*c_void, &root_signature) }); | |
if (hr < 0) { | |
return error.RootSignatureCreationFailed; | |
} | |
// Create vertex and pixel shader | |
var vertex_shader: ?*d3d12.ID3DBlob = null; | |
var error_buffer: ?*d3d12.ID3DBlob = null; | |
hr = d3dcompiler.D3DCompileFromFile( | |
L("data/shaders/simple_vs.hlsl"), | |
null, | |
null, | |
"main", | |
"vs_5_0", | |
d3dcompiler.D3DCOMPILE_DEBUG | d3dcompiler.D3DCOMPILE_SKIP_OPTIMIZATION, | |
0, | |
@ptrCast([*c][*c]d3dcompiler.ID3DBlob, &vertex_shader), | |
@ptrCast([*c][*c]d3dcompiler.ID3DBlob, &error_buffer), | |
); | |
if (hr < 0) { | |
std.log.err("Vertex shader compilation failed:\n{}", .{@ptrCast([*:0]const u8, comCall(error_buffer, "GetBufferPointer", .{}))}); | |
return error.VertexShaderCompilationFailed; | |
} | |
var vertex_shader_bytecode: d3d12.D3D12_SHADER_BYTECODE = undefined; | |
vertex_shader_bytecode.BytecodeLength = comCall(vertex_shader, "GetBufferSize", .{}); | |
vertex_shader_bytecode.pShaderBytecode = comCall(vertex_shader, "GetBufferPointer", .{}); | |
var pixel_shader: ?*d3d12.ID3DBlob = null; | |
hr = d3dcompiler.D3DCompileFromFile( | |
L("data/shaders/simple_ps.hlsl"), | |
null, | |
null, | |
"main", | |
"ps_5_0", | |
d3dcompiler.D3DCOMPILE_DEBUG | d3dcompiler.D3DCOMPILE_SKIP_OPTIMIZATION, | |
0, | |
@ptrCast([*c][*c]d3dcompiler.ID3DBlob, &pixel_shader), | |
@ptrCast([*c][*c]d3dcompiler.ID3DBlob, &error_buffer), | |
); | |
if (hr < 0) { | |
std.log.err("Pixel shader compilation failed:\n{}", .{@ptrCast([*:0]const u8, comCall(error_buffer, "GetBufferPointer", .{}))}); | |
return error.PixelShaderCompilationFailed; | |
} | |
var pixel_shader_bytecode: d3d12.D3D12_SHADER_BYTECODE = undefined; | |
pixel_shader_bytecode.BytecodeLength = comCall(pixel_shader, "GetBufferSize", .{}); | |
pixel_shader_bytecode.pShaderBytecode = comCall(pixel_shader, "GetBufferPointer", .{}); | |
// Create input layout | |
var input_layout_desc: d3d12.D3D12_INPUT_LAYOUT_DESC = undefined; | |
input_layout_desc.NumElements = input_layout.len; | |
input_layout_desc.pInputElementDescs = &input_layout[0]; | |
// Create the pipeline state object | |
var pso_desc: d3d12.D3D12_GRAPHICS_PIPELINE_STATE_DESC = std.mem.zeroes(d3d12.D3D12_GRAPHICS_PIPELINE_STATE_DESC); | |
pso_desc.InputLayout = input_layout_desc; | |
pso_desc.pRootSignature = root_signature; | |
pso_desc.VS = vertex_shader_bytecode; | |
pso_desc.PS = pixel_shader_bytecode; | |
pso_desc.PrimitiveTopologyType = ._TRIANGLE; | |
pso_desc.RTVFormats[0] = ._R8G8B8A8_UNORM; | |
pso_desc.SampleDesc.Count = 1; | |
pso_desc.SampleMask = 0xffffffff; | |
pso_desc.RasterizerState = .{ | |
.FillMode = ._SOLID, | |
.CullMode = ._BACK, | |
.FrontCounterClockwise = @boolToInt(false), | |
.DepthBias = d3d12.D3D12_DEFAULT_DEPTH_BIAS, | |
.DepthBiasClamp = d3d12.D3D12_DEFAULT_DEPTH_BIAS_CLAMP, | |
.SlopeScaledDepthBias = d3d12.D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, | |
.DepthClipEnable = @boolToInt(true), | |
.MultisampleEnable = @boolToInt(false), | |
.AntialiasedLineEnable = @boolToInt(false), | |
.ForcedSampleCount = 0, | |
.ConservativeRaster = ._OFF, | |
}; | |
pso_desc.BlendState = .{ | |
.AlphaToCoverageEnable = @boolToInt(false), | |
.IndependentBlendEnable = @boolToInt(false), | |
.RenderTarget = [_]d3d12.D3D12_RENDER_TARGET_BLEND_DESC{.{ | |
.BlendEnable = @boolToInt(false), | |
.LogicOpEnable = @boolToInt(false), | |
.SrcBlend = ._ONE, | |
.DestBlend = ._ZERO, | |
.BlendOp = ._ADD, | |
.SrcBlendAlpha = ._ONE, | |
.DestBlendAlpha = ._ZERO, | |
.BlendOpAlpha = ._ADD, | |
.LogicOp = ._NOOP, | |
.RenderTargetWriteMask = d3d12.D3D12_COLOR_WRITE_ENABLE_ALL, | |
}} ** 8, | |
}; | |
const default_stencil_op = d3d12.D3D12_DEPTH_STENCILOP_DESC{ | |
.StencilFailOp = ._KEEP, | |
.StencilDepthFailOp = ._KEEP, | |
.StencilPassOp = ._KEEP, | |
.StencilFunc = ._ALWAYS, | |
}; | |
pso_desc.DepthStencilState = .{ | |
.DepthEnable = @boolToInt(true), | |
.DepthWriteMask = ._ALL, | |
.DepthFunc = ._LESS, | |
.StencilEnable = @boolToInt(false), | |
.StencilReadMask = d3d12.D3D12_DEFAULT_STENCIL_READ_MASK, | |
.StencilWriteMask = d3d12.D3D12_DEFAULT_STENCIL_WRITE_MASK, | |
.FrontFace = default_stencil_op, | |
.BackFace = default_stencil_op, | |
}; | |
pso_desc.NumRenderTargets = 1; | |
hr = comCall(device, "CreateGraphicsPipelineState", .{ &pso_desc, uuidof(d3d12, d3d12.ID3D12PipelineState), @ptrCast([*c]?*c_void, &pipeline_state_object) }); | |
if (hr < 0) { | |
return error.PipelineStateCreationFailed; | |
} | |
const vertex_buffer_size = bufferSize(quad_vertices); | |
// Create a vertex buffer | |
const default_heap_properties: d3d12.D3D12_HEAP_PROPERTIES = .{ | |
.Type = ._DEFAULT, | |
.CPUPageProperty = ._UNKNOWN, | |
.MemoryPoolPreference = ._UNKNOWN, | |
.CreationNodeMask = 1, | |
.VisibleNodeMask = 1, | |
}; | |
const upload_heap_properties: d3d12.D3D12_HEAP_PROPERTIES = .{ | |
.Type = ._UPLOAD, | |
.CPUPageProperty = ._UNKNOWN, | |
.MemoryPoolPreference = ._UNKNOWN, | |
.CreationNodeMask = 1, | |
.VisibleNodeMask = 1, | |
}; | |
const vertex_resource_desc: d3d12.D3D12_RESOURCE_DESC = .{ | |
.Dimension = ._BUFFER, | |
.Alignment = 0, | |
.Width = vertex_buffer_size, | |
.Height = 1, | |
.DepthOrArraySize = 1, | |
.MipLevels = 1, | |
.Format = ._UNKNOWN, | |
.SampleDesc = d3d12.DXGI_SAMPLE_DESC{ | |
.Count = 1, | |
.Quality = 0, | |
}, | |
.Layout = ._ROW_MAJOR, | |
.Flags = .D3D12_RESOURCE_FLAG_NONE, | |
}; | |
hr = comCall(device, "CreateCommittedResource", .{ &default_heap_properties, .D3D12_HEAP_FLAG_NONE, &vertex_resource_desc, .D3D12_RESOURCE_STATE_COPY_DEST, null, uuidof(d3d12, d3d12.ID3D12Resource), @ptrCast([*c]?*c_void, &vertex_buffer) }); | |
if (hr < 0) { | |
return error.VertexBufferCreationFailed; | |
} | |
_ = comCall(vertex_buffer, "SetName", .{L("Vertex Buffer Resource Heap")}); | |
var vertex_buffer_upload_heap: ?*d3d12.ID3D12Resource = null; | |
hr = comCall(device, "CreateCommittedResource", .{ &upload_heap_properties, .D3D12_HEAP_FLAG_NONE, &vertex_resource_desc, .D3D12_RESOURCE_STATE_GENERIC_READ, null, uuidof(d3d12, d3d12.ID3D12Resource), @ptrCast([*c]?*c_void, &vertex_buffer_upload_heap) }); | |
if (hr < 0) { | |
return error.VertexBufferUploadHeapCreationFailed; | |
} | |
_ = comCall(vertex_buffer_upload_heap, "SetName", .{L("Vertex Buffer Upload Resource Heap")}); | |
var vertex_data: d3d12.D3D12_SUBRESOURCE_DATA = .{ | |
.pData = &quad_vertices[0], | |
.RowPitch = @intCast(c_longlong, vertex_buffer_size), | |
.SlicePitch = @intCast(c_longlong, vertex_buffer_size), | |
}; | |
_ = try updateSubresourcesAlloc(std.heap.c_allocator, command_list, vertex_buffer, vertex_buffer_upload_heap, 0, 0, 1, @ptrCast([*]d3d12.D3D12_SUBRESOURCE_DATA, &vertex_data)); | |
const vertex_barrier = resourceBarrierTransition(vertex_buffer, .D3D12_RESOURCE_STATE_COPY_DEST, .D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); | |
comCall(command_list, "ResourceBarrier", .{ 1, &vertex_barrier }); | |
// Create index buffer | |
const index_buffer_size = bufferSize(quad_indices); | |
const index_resource_desc: d3d12.D3D12_RESOURCE_DESC = .{ | |
.Dimension = ._BUFFER, | |
.Alignment = 0, | |
.Width = index_buffer_size, | |
.Height = 1, | |
.DepthOrArraySize = 1, | |
.MipLevels = 1, | |
.Format = ._UNKNOWN, | |
.SampleDesc = d3d12.DXGI_SAMPLE_DESC{ | |
.Count = 1, | |
.Quality = 0, | |
}, | |
.Layout = ._ROW_MAJOR, | |
.Flags = .D3D12_RESOURCE_FLAG_NONE, | |
}; | |
hr = comCall(device, "CreateCommittedResource", .{ &default_heap_properties, .D3D12_HEAP_FLAG_NONE, &index_resource_desc, .D3D12_RESOURCE_STATE_COPY_DEST, null, uuidof(d3d12, d3d12.ID3D12Resource), @ptrCast([*c]?*c_void, &index_buffer) }); | |
if (hr < 0) { | |
return error.IndexBufferCreationFailed; | |
} | |
_ = comCall(index_buffer, "SetName", .{L("Index Buffer Resource Heap")}); | |
var index_buffer_upload_heap: ?*d3d12.ID3D12Resource = null; | |
hr = comCall(device, "CreateCommittedResource", .{ &upload_heap_properties, .D3D12_HEAP_FLAG_NONE, &index_resource_desc, .D3D12_RESOURCE_STATE_GENERIC_READ, null, uuidof(d3d12, d3d12.ID3D12Resource), @ptrCast([*c]?*c_void, &index_buffer_upload_heap) }); | |
if (hr < 0) { | |
return error.IndexBufferUploadHeapCreationFailed; | |
} | |
_ = comCall(index_buffer_upload_heap, "SetName", .{L("Index Buffer Upload Resource Heap")}); | |
var indices_data: d3d12.D3D12_SUBRESOURCE_DATA = .{ | |
.pData = &quad_indices[0], | |
.RowPitch = @intCast(c_longlong, index_buffer_size), | |
.SlicePitch = @intCast(c_longlong, index_buffer_size), | |
}; | |
_ = try updateSubresourcesAlloc(std.heap.c_allocator, command_list, index_buffer, index_buffer_upload_heap, 0, 0, 1, @ptrCast([*]d3d12.D3D12_SUBRESOURCE_DATA, &indices_data)); | |
const indices_barrier = resourceBarrierTransition(index_buffer, .D3D12_RESOURCE_STATE_COPY_DEST, .D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); | |
comCall(command_list, "ResourceBarrier", .{ 1, &indices_barrier }); | |
index_buffer_view.BufferLocation = comCall(index_buffer, "GetGPUVirtualAddress", .{}); | |
index_buffer_view.Format = ._R32_UINT; | |
index_buffer_view.SizeInBytes = @intCast(c_uint, index_buffer_size); | |
_ = comCall(command_list, "Close", .{}); | |
var command_lists: ?*d3d12.ID3D12CommandList = null; | |
getComInteface(command_list, uuidof(d3d12, d3d12.ID3D12CommandList), &command_lists) catch unreachable; | |
comCall(command_queue, "ExecuteCommandLists", .{ 1, @ptrCast([*c]const [*c]d3d12.ID3D12CommandList, &command_lists) }); | |
fence_value[frame_index] += 1; | |
hr = comCall(command_queue, "Signal", .{ fence[frame_index], fence_value[frame_index] }); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
// Create Depth/Stencil buffer and view | |
var dsv_heap_desc = d3d12.D3D12_DESCRIPTOR_HEAP_DESC{ | |
.NumDescriptors = 1, | |
.Type = ._DSV, | |
.Flags = .D3D12_DESCRIPTOR_HEAP_FLAG_NONE, | |
.NodeMask = 0, | |
}; | |
hr = comCall(device, "CreateDescriptorHeap", .{ &dsv_heap_desc, uuidof(d3d12, d3d12.ID3D12DescriptorHeap), @ptrCast([*c]?*c_void, &depth_descriptor_heap) }); | |
if (hr < 0) { | |
return error.DepthStencilDescriptorHeapCreationFailed; | |
} | |
var depth_stencil_desc: d3d12.D3D12_DEPTH_STENCIL_VIEW_DESC = std.mem.zeroes(d3d12.D3D12_DEPTH_STENCIL_VIEW_DESC); | |
depth_stencil_desc.Format = ._D32_FLOAT; | |
depth_stencil_desc.ViewDimension = ._TEXTURE2D; | |
depth_stencil_desc.Flags = .D3D12_DSV_FLAG_NONE; | |
var depth_stencil_clear_value = d3d12.D3D12_CLEAR_VALUE{ | |
.Format = ._D32_FLOAT, | |
.unnamed_0 = .{ | |
.DepthStencil = .{ | |
.Depth = 1.0, | |
.Stencil = 0, | |
}, | |
}, | |
}; | |
const depth_resource_desc: d3d12.D3D12_RESOURCE_DESC = .{ | |
.Dimension = ._TEXTURE2D, | |
.Alignment = 0, | |
.Width = WindowWidth, | |
.Height = WindowHeight, | |
.DepthOrArraySize = 1, | |
.MipLevels = 0, | |
.Format = ._D32_FLOAT, | |
.SampleDesc = d3d12.DXGI_SAMPLE_DESC{ | |
.Count = 1, | |
.Quality = 0, | |
}, | |
.Layout = ._UNKNOWN, | |
.Flags = .D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL, | |
}; | |
hr = comCall(device, "CreateCommittedResource", .{ &default_heap_properties, .D3D12_HEAP_FLAG_NONE, &depth_resource_desc, .D3D12_RESOURCE_STATE_DEPTH_WRITE, &depth_stencil_clear_value, uuidof(d3d12, d3d12.ID3D12Resource), @ptrCast([*c]?*c_void, &depth_stencil_buffer) }); | |
if (hr < 0) { | |
return error.DepthStencilBufferCreationFailed; | |
} | |
_ = comCall(depth_stencil_buffer, "SetName", .{L("Depth/Stencil Resource Heap")}); | |
var depth_stencil_handle: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE = undefined; | |
comCall(depth_descriptor_heap, "GetCPUDescriptorHandleForHeapStart", .{&depth_stencil_handle}); | |
comCall(device, "CreateDepthStencilView", .{ depth_stencil_buffer, &depth_stencil_desc, depth_stencil_handle }); | |
// Create a vertex buffer view for the triangle. | |
vertex_buffer_view.BufferLocation = comCall(vertex_buffer, "GetGPUVirtualAddress", .{}); | |
vertex_buffer_view.StrideInBytes = @sizeOf(Vertex); | |
vertex_buffer_view.SizeInBytes = @intCast(c_uint, vertex_buffer_size); | |
// Fill viewport | |
viewport.TopLeftX = 0; | |
viewport.TopLeftY = 0; | |
viewport.Width = WindowWidth; | |
viewport.Height = WindowHeight; | |
viewport.MinDepth = 0.0; | |
viewport.MaxDepth = 1.0; | |
// Fill scissor rect | |
scissor_rect.left = 0; | |
scissor_rect.top = 0; | |
scissor_rect.right = WindowWidth; | |
scissor_rect.bottom = WindowHeight; | |
} | |
fn resourceBarrierTransition(resource: ?*d3d12.ID3D12Resource, state_before: d3d12.D3D12_RESOURCE_STATES, state_after: d3d12.D3D12_RESOURCE_STATES) d3d12.D3D12_RESOURCE_BARRIER { | |
var result: d3d12.D3D12_RESOURCE_BARRIER = std.mem.zeroes(d3d12.D3D12_RESOURCE_BARRIER); | |
result.Type = ._TRANSITION; | |
result.Flags = .D3D12_RESOURCE_BARRIER_FLAG_NONE; | |
result.unnamed_0.Transition.pResource = resource.?; | |
result.unnamed_0.Transition.StateBefore = state_before; | |
result.unnamed_0.Transition.StateAfter = state_after; | |
result.unnamed_0.Transition.Subresource = d3d12.D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; | |
return result; | |
} | |
fn quitApplication() void { | |
_ = win32.PostMessageA(hwnd, win32.WM_CLOSE, 0, 0); | |
} | |
fn update() void {} | |
fn updatePipeline() void { | |
var hr: win32.HRESULT = undefined; | |
waitForPreviousFrame(); | |
hr = comCall(command_allocator[frame_index], "Reset", .{}); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
hr = comCall(command_list, "Reset", .{ command_allocator[frame_index], pipeline_state_object }); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
const first_barrier = resourceBarrierTransition(render_targets[frame_index], .D3D12_RESOURCE_STATE_PRESENT, .D3D12_RESOURCE_STATE_RENDER_TARGET); | |
const second_barrier = resourceBarrierTransition(render_targets[frame_index], .D3D12_RESOURCE_STATE_RENDER_TARGET, .D3D12_RESOURCE_STATE_PRESENT); | |
comCall(command_list, "ResourceBarrier", .{ 1, &first_barrier }); | |
var rtv_handle: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE = undefined; | |
comCall(rtv_descriptor_heap, "GetCPUDescriptorHandleForHeapStart", .{&rtv_handle}); | |
rtv_handle.ptr += frame_index * rtv_descriptor_size; | |
var depth_stencil_handle: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE = undefined; | |
comCall(depth_descriptor_heap, "GetCPUDescriptorHandleForHeapStart", .{&depth_stencil_handle}); | |
comCall(command_list, "OMSetRenderTargets", .{ 1, &rtv_handle, @boolToInt(false), &depth_stencil_handle }); | |
const clear_color = [_]f32{ 0.0, 0.2, 0.4, 1.0 }; | |
comCall(command_list, "ClearRenderTargetView", .{ rtv_handle, &clear_color, 0, null }); | |
comCall(command_list, "ClearDepthStencilView", .{ depth_stencil_handle, .D3D12_CLEAR_FLAG_DEPTH, 1.0, 0, 0, null }); | |
comCall(command_list, "SetGraphicsRootSignature", .{root_signature}); | |
comCall(command_list, "RSSetViewports", .{ 1, &viewport }); | |
comCall(command_list, "RSSetScissorRects", .{ 1, &scissor_rect }); | |
comCall(command_list, "IASetPrimitiveTopology", .{._TRIANGLELIST}); | |
comCall(command_list, "IASetVertexBuffers", .{ 0, 1, &vertex_buffer_view }); | |
comCall(command_list, "IASetIndexBuffer", .{&index_buffer_view}); | |
comCall(command_list, "DrawIndexedInstanced", .{ 6, 1, 0, 0, 0 }); | |
comCall(command_list, "DrawIndexedInstanced", .{ 6, 1, 0, 4, 0 }); | |
comCall(command_list, "ResourceBarrier", .{ 1, &second_barrier }); | |
hr = comCall(command_list, "Close", .{}); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
} | |
fn render() void { | |
var hr: win32.HRESULT = undefined; | |
updatePipeline(); | |
var command_lists: ?*d3d12.ID3D12CommandList = null; | |
getComInteface(command_list, uuidof(d3d12, d3d12.ID3D12CommandList), &command_lists) catch unreachable; | |
comCall(command_queue, "ExecuteCommandLists", .{ 1, @ptrCast([*c]const [*c]d3d12.ID3D12CommandList, &command_lists) }); | |
hr = comCall(command_queue, "Signal", .{ fence[frame_index], fence_value[frame_index] }); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
hr = comCall(swap_chain, "Present", .{ 0, 0 }); | |
if (hr < 0) { | |
quitApplication(); | |
} | |
} | |
fn cleanup() void { | |
var index: usize = 0; | |
while (index < FrameBufferCount) : (index += 1) { | |
frame_index = index; | |
waitForPreviousFrame(); | |
} | |
var fs: c_int = 0; | |
if (comCall(swap_chain, "GetFullscreenState", .{ &fs, null }) >= 0) { | |
_ = comCall(swap_chain, "SetFullscreenState", .{ @boolToInt(false), null }); | |
} | |
comRelease(device); | |
comRelease(swap_chain); | |
comRelease(command_queue); | |
comRelease(rtv_descriptor_heap); | |
comRelease(command_list); | |
index = 0; | |
while (index < FrameBufferCount) : (index += 1) { | |
comRelease(render_targets[index]); | |
comRelease(command_allocator[index]); | |
comRelease(fence[index]); | |
} | |
comRelease(pipeline_state_object); | |
comRelease(root_signature); | |
comRelease(vertex_buffer); | |
comRelease(index_buffer); | |
comRelease(depth_stencil_buffer); | |
comRelease(depth_descriptor_heap); | |
} | |
fn waitForPreviousFrame() void { | |
var hr: win32.HRESULT = undefined; | |
frame_index = comCall(swap_chain, "GetCurrentBackBufferIndex", .{}); | |
const completed_value = comCall(fence[frame_index], "GetCompletedValue", .{}); | |
if (completed_value < fence_value[frame_index]) { | |
hr = comCall(fence[frame_index], "SetEventOnCompletion", .{ fence_value[frame_index], fence_event }); | |
if (hr < 0) { | |
// Workaround | |
render_targets[frame_index] = null; | |
command_allocator[frame_index] = null; | |
fence[frame_index] = null; | |
return; | |
} | |
_ = win32.WaitForSingleObject(fence_event, win32.INFINITE); | |
} | |
fence_value[frame_index] += 1; | |
} | |
fn WndProc(hWnd: win32.HWND, msg: win32.UINT, wParam: win32.WPARAM, lParam: win32.LPARAM) callconv(.C) win32.LRESULT { | |
switch (msg) { | |
win32.WM_SYSCOMMAND => { | |
if ((wParam & 0xfff0) == win32.SC_KEYMENU) { | |
return 0; | |
} | |
}, | |
win32.WM_DESTROY => { | |
_ = win32.PostQuitMessage(0); | |
return 0; | |
}, | |
win32.WM_KEYDOWN => { | |
if (wParam == win32.VK_ESCAPE) { | |
if (win32.MessageBox(null, "Are you sure you want to exit ?", "Really?", win32.MB_YESNO | win32.MB_ICONQUESTION) == win32.IDYES) { | |
_ = win32.DestroyWindow(hWnd); | |
} | |
} | |
}, | |
else => {}, | |
} | |
return win32.DefWindowProc(hWnd, msg, wParam, lParam); | |
} | |
pub fn init() !void { | |
const winClass: win32.WNDCLASSEX = .{ | |
.cbSize = @sizeOf(win32.WNDCLASSEX), | |
.style = win32.CS_HREDRAW | win32.CS_VREDRAW, | |
.lpfnWndProc = WndProc, | |
.cbClsExtra = 0, | |
.cbWndExtra = 0, | |
.hInstance = win32.GetModuleHandle(null), | |
.hIcon = null, | |
.hCursor = null, | |
.hbrBackground = null, | |
.lpszMenuName = null, | |
.lpszClassName = "DefaultHochelagWindow", | |
.hIconSm = null, | |
}; | |
var x: i32 = WindowX; | |
var y: i32 = WindowY; | |
var width: i32 = WindowWidth; | |
var height: i32 = WindowHeight; | |
if (Fullscreen) { | |
const hMon = win32.MonitorFromWindow(null, win32.MONITOR_DEFAULTTONEAREST); | |
var monitorInfo = std.mem.zeroes(win32.MONITORINFO); | |
_ = win32.GetMonitorInfo(hMon, &monitorInfo); | |
x = 0; | |
y = 0; | |
width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; | |
height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; | |
} | |
_ = win32.RegisterClassEx(&winClass); | |
defer _ = win32.UnregisterClass(winClass.lpszClassName, winClass.hInstance); | |
hwnd = win32.CreateWindowEx(0, winClass.lpszClassName, "Hochelag Direct3D 12 Playground", win32.WS_OVERLAPPEDWINDOW, x, y, width, height, null, null, winClass.hInstance, null); | |
if (hwnd == null) { | |
std.debug.warn("Error creating Win32 Window = 0x{x}\n", .{@enumToInt(windows.kernel32.GetLastError())}); | |
return error.InvalidWin32Window; | |
} | |
defer _ = win32.DestroyWindow(hwnd); | |
if (Fullscreen) { | |
_ = win32.SetWindowLong(hwnd, win32.GWL_STYLE, 0); | |
} | |
_ = win32.ShowWindow(hwnd, win32.SW_SHOWDEFAULT); | |
_ = win32.UpdateWindow(hwnd); | |
try initDirect3D(); | |
var msg: win32.MSG = std.mem.zeroes(win32.MSG); | |
while (msg.message != win32.WM_QUIT) { | |
if (win32.PeekMessage(&msg, null, 0, 0, win32.PM_REMOVE) != 0) { | |
_ = win32.TranslateMessage(&msg); | |
_ = win32.DispatchMessage(&msg); | |
continue; | |
} | |
update(); | |
render(); | |
} | |
waitForPreviousFrame(); | |
windows.CloseHandle(fence_event.?); | |
cleanup(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment