Skip to content

Instantly share code, notes, and snippets.

@marler8997
Created January 11, 2025 02:37
Show Gist options
  • Save marler8997/b468f44b654858cc17269ea2be81c524 to your computer and use it in GitHub Desktop.
Save marler8997/b468f44b654858cc17269ea2be81c524 to your computer and use it in GitHub Desktop.
Zig Refterm Colored Cells Example
const std = @import("std");
const builtin = @import("builtin");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const win32_dep = b.dependency("win32", .{});
const win32_mod = win32_dep.module("zigwin32");
const exe = b.addExecutable(.{
.name = "example",
.root_source_file = b.path("example.zig"),
.target = target,
.win32_manifest = b.path("example.manifest"),
});
exe.root_module.addImport("win32", win32_mod);
const install = b.addInstallArtifact(exe, .{});
const run = b.addRunArtifact(exe);
run.step.dependOn(&install.step);
b.step("run", "").dependOn(&run.step);
}
.{
.name = "example",
.version = "0.0.0",
.minimum_zig_version = "0.13.0",
.dependencies = .{
.win32 = .{
.url = "git+https://github.com/marlersoft/zigwin32#73de834b8c062849320148bfb3d872bf55146c01",
.hash = "122093b6b38b11c27a48c4822511aa41b4fff36719f2cb3bb7fe0f8aec2f7b89fa52",
},
},
.paths = .{
".",
},
}
cbuffer GridConfig : register(b0)
{
uint2 cell_size;
uint2 viewport_cell_size;
}
struct Cell
{
uint glyph_index;
uint background;
uint foreground;
// todo: underline flags, single/double/curly/dotted/dashed
// todo: underline color
};
StructuredBuffer<Cell> cells : register(t0);
Texture2D<float4> glyph_texture : register(t1);
float4 VertexMain(uint id : SV_VERTEXID) : SV_POSITION
{
return float4(
2.0 * (float(id & 1) - 0.5),
-(float(id >> 1) - 0.5) * 2.0,
0, 1
);
}
float4 UnpackRgba(uint packed)
{
float4 unpacked;
unpacked.r = (float)((packed >> 24) & 0xFF) / 255.0f;
unpacked.g = (float)((packed >> 16) & 0xFF) / 255.0f;
unpacked.b = (float)((packed >> 8) & 0xFF) / 255.0f;
unpacked.a = (float)(packed & 0xFF) / 255.0f;
return unpacked;
}
float4 PixelMain(float4 sv_pos : SV_POSITION) : SV_TARGET {
uint2 grid_pos = sv_pos.xy / cell_size;
uint index = grid_pos.y * viewport_cell_size.x + grid_pos.x;
bool show_checkerboard = false;
if (show_checkerboard) {
uint cell_count = viewport_cell_size.x * viewport_cell_size.y;
float strength = float(index) / float(cell_count);
uint checker = (grid_pos.x + grid_pos.y) % 2;
if (checker == 0) {
float shade = 1.0 - strength;
return float4(shade,shade,shade,1);
}
return float4(0,0,0,1);
}
// uncomment to just display the cache texture
// return glyph_texture[sv_pos.xy].rgba;
return UnpackRgba(cells[index].background);
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
</dependentAssembly>
</dependency>
</assembly>
const std = @import("std");
const win32 = @import("win32").everything;
const window_style_ex: win32.WINDOW_EX_STYLE = .{
.APPWINDOW = 1,
// the redirection bitmap is unnecessary for a d3d window and causes
// bad artifacts when the window is resized
.NOREDIRECTIONBITMAP = 1,
};
const window_style = win32.WS_OVERLAPPEDWINDOW;
const GridConfig = extern struct {
cell_size: [2]u32,
viewport_cell_size: [2]u32,
};
const Cell = extern struct {
glyph_index: u32,
background: u32,
foreground: u32,
};
const global = struct {
var d3d: D3d = undefined;
var shaders: Shaders = undefined;
var const_buf: *win32.ID3D11Buffer = undefined;
var state: ?State = null;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// TODO: remove
var rand: std.Random.DefaultPrng = undefined;
};
// TODO: DXGI_SWAP_CHAIN_FLAG needs to be marked as a flags enum
//const swap_chain_flags: u32 = 0;
const swap_chain_flags: u32 = @intFromEnum(win32.DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
const D3d = struct {
device: *win32.ID3D11Device,
context: *win32.ID3D11DeviceContext,
context1: *win32.ID3D11DeviceContext1,
};
const State = struct {
hwnd: win32.HWND,
swap_chain: *win32.IDXGISwapChain2,
maybe_target_view: ?*win32.ID3D11RenderTargetView = null,
shader_cells: ShaderCells,
glyph_texture: GlyphTexture,
};
fn stateFromHwnd(hwnd: win32.HWND) *State {
std.debug.assert(global.state.?.hwnd == hwnd);
return &(global.state.?);
}
pub fn main() anyerror!void {
global.rand = std.Random.DefaultPrng.init(win32.GetTickCount());
const CLASS_NAME = win32.L("D3dExample");
const wc = win32.WNDCLASSEXW{
.cbSize = @sizeOf(win32.WNDCLASSEXW),
.style = .{ .VREDRAW = 1, .HREDRAW = 1 },
.lpfnWndProc = WndProc,
.cbClsExtra = 0,
.cbWndExtra = 0,
.hInstance = win32.GetModuleHandleW(null),
.hIcon = null,
.hCursor = win32.LoadCursorW(null, win32.IDC_ARROW),
.hbrBackground = null,
.lpszMenuName = null,
.lpszClassName = CLASS_NAME,
.hIconSm = null,
};
if (0 == win32.RegisterClassExW(&wc)) fatalWin32(
"RegisterClass for main window",
win32.GetLastError(),
);
const debug_d3d = true;
global.d3d = initD3d(.{ .debug = debug_d3d });
if (debug_d3d) {
const info = queryInterface(global.d3d.device, win32.ID3D11InfoQueue);
defer _ = info.IUnknown.Release();
{
const hr = info.SetBreakOnSeverity(.CORRUPTION, 1);
if (hr < 0) fatalHr("SetBreakOnCorruption", hr);
}
{
const hr = info.SetBreakOnSeverity(.ERROR, 1);
if (hr < 0) fatalHr("SetBreakOnError", hr);
}
{
const hr = info.SetBreakOnSeverity(.WARNING, 1);
if (hr < 0) fatalHr("SetBreakOnWarning", hr);
}
}
global.shaders = Shaders.init();
{
const buffer_desc: win32.D3D11_BUFFER_DESC = .{
// d3d requires constants be sized in multiples of 16
.ByteWidth = std.mem.alignForward(u32, @sizeOf(GridConfig), 16),
.Usage = .DYNAMIC,
.BindFlags = .{ .CONSTANT_BUFFER = 1 },
.CPUAccessFlags = .{ .WRITE = 1 },
.MiscFlags = .{},
.StructureByteStride = 0,
};
const hr = global.d3d.device.CreateBuffer(&buffer_desc, null, &global.const_buf);
if (hr < 0) fatalHr("CreateBuffer for grid config", hr);
}
const hwnd = win32.CreateWindowExW(
window_style_ex,
CLASS_NAME, // Window class
win32.L("D3d Example"),
window_style,
100,
100,
//800,
920,
600,
null, // Parent window
null, // Menu
win32.GetModuleHandleW(null),
null,
) orelse fatalWin32("CreateWindow", win32.GetLastError());
if (0 == win32.UpdateWindow(hwnd)) fatalWin32("UpdateWindow", win32.GetLastError());
_ = win32.ShowWindow(hwnd, win32.SW_SHOWNORMAL);
var msg: win32.MSG = undefined;
while (win32.GetMessageW(&msg, null, 0, 0) != 0) {
_ = win32.TranslateMessage(&msg);
_ = win32.DispatchMessageW(&msg);
}
}
fn initD3d(opt: struct { debug: bool }) D3d {
const levels = [_]win32.D3D_FEATURE_LEVEL{
.@"11_0",
};
var last_hr: i32 = undefined;
for (&[_]win32.D3D_DRIVER_TYPE{ .HARDWARE, .WARP }) |driver| {
var device: *win32.ID3D11Device = undefined;
var context: *win32.ID3D11DeviceContext = undefined;
last_hr = win32.D3D11CreateDevice(
null,
driver,
null,
.{
.BGRA_SUPPORT = 1,
.SINGLETHREADED = 1,
.DEBUG = if (opt.debug) 1 else 0,
},
&levels,
levels.len,
win32.D3D11_SDK_VERSION,
&device,
null,
&context,
);
if (last_hr >= 0) return .{
.device = device,
.context = context,
.context1 = queryInterface(context, win32.ID3D11DeviceContext1),
};
std.log.info(
"D3D11 {s} Driver error, hresult=0x{x}",
.{ @tagName(driver), @as(u32, @bitCast(last_hr)) },
);
}
std.debug.panic("failed to initialize Direct3D11, hresult=0x{x}", .{last_hr});
}
fn getDxgiFactory(device: *win32.ID3D11Device) *win32.IDXGIFactory2 {
const dxgi_device = queryInterface(device, win32.IDXGIDevice);
defer _ = dxgi_device.IUnknown.Release();
var adapter: *win32.IDXGIAdapter = undefined;
{
const hr = dxgi_device.GetAdapter(&adapter);
if (hr < 0) fatalHr("GetDxgiAdapter", hr);
}
defer _ = adapter.IUnknown.Release();
var factory: *win32.IDXGIFactory2 = undefined;
{
const hr = adapter.IDXGIObject.GetParent(win32.IID_IDXGIFactory2, @ptrCast(&factory));
if (hr < 0) fatalHr("GetDxgiFactory", hr);
}
return factory;
}
fn getSwapChainSize(swap_chain: *win32.IDXGISwapChain2) XY(u32) {
var size: XY(u32) = undefined;
{
const hr = swap_chain.GetSourceSize(&size.x, &size.y);
if (hr < 0) fatalHr("GetSwapChainSourceSize", hr);
}
return size;
}
fn initSwapChain(
device: *win32.ID3D11Device,
hwnd: win32.HWND,
) *win32.IDXGISwapChain2 {
const factory = getDxgiFactory(device);
defer _ = factory.IUnknown.Release();
const swap_chain1: *win32.IDXGISwapChain1 = blk: {
var swap_chain1: *win32.IDXGISwapChain1 = undefined;
const desc = win32.DXGI_SWAP_CHAIN_DESC1{
.Width = 0,
.Height = 0,
.Format = .B8G8R8A8_UNORM,
.Stereo = 0,
.SampleDesc = .{ .Count = 1, .Quality = 0 },
.BufferUsage = win32.DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferCount = 2,
// TODO: we might want to call SetBackgroundColor afterwards as that's what will be
// rendered outside the swap chain buffer
.Scaling = .NONE,
.SwapEffect = .FLIP_DISCARD,
.AlphaMode = .IGNORE,
.Flags = swap_chain_flags,
};
{
const hr = factory.CreateSwapChainForHwnd(
&device.IUnknown,
hwnd,
&desc,
null,
null,
&swap_chain1,
);
if (hr < 0) fatalHr("CreateD3dSwapChain", hr);
}
break :blk swap_chain1;
};
defer _ = swap_chain1.IUnknown.Release();
var swap_chain2: *win32.IDXGISwapChain2 = undefined;
{
const hr = swap_chain1.IUnknown.QueryInterface(win32.IID_IDXGISwapChain2, @ptrCast(&swap_chain2));
if (hr < 0) fatalHr("QuerySwapChain2", hr);
}
// refterm is doing this but I don't know why
if (false) {
const hr = factory.IDXGIFactory.MakeWindowAssociation(hwnd, 0); //DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);
if (hr < 0) fatalHr("MakeWindowAssoc", hr);
}
return swap_chain2;
}
// TODO: update zigwin32 with a way to get the corresponding IID for any COM interface
fn queryInterface(obj: anytype, comptime Interface: type) *Interface {
const obj_basename_start: usize = comptime if (std.mem.lastIndexOfScalar(u8, @typeName(@TypeOf(obj)), '.')) |i| (i + 1) else 0;
const obj_basename = @typeName(@TypeOf(obj))[obj_basename_start..];
const iface_basename_start: usize = comptime if (std.mem.lastIndexOfScalar(u8, @typeName(Interface), '.')) |i| (i + 1) else 0;
const iface_basename = @typeName(Interface)[iface_basename_start..];
const iid_name = "IID_" ++ iface_basename;
const iid = @field(win32, iid_name);
var iface: *Interface = undefined;
const hr = obj.IUnknown.QueryInterface(iid, @ptrCast(&iface));
if (hr < 0) std.debug.panic(
"QueryInferface on " ++ obj_basename ++ " as " ++ iface_basename ++ " failed, hresult={}",
.{@as(u32, @bitCast(hr))},
);
return iface;
}
const Shaders = struct {
vertex: *win32.ID3D11VertexShader,
pixel: *win32.ID3D11PixelShader,
pub fn init() Shaders {
const shader_source = @embedFile("example.hlsl");
var vs_blob: *win32.ID3DBlob = undefined;
var error_blob: ?*win32.ID3DBlob = null;
{
const hr = win32.D3DCompile(
shader_source.ptr,
shader_source.len,
null,
null,
null,
"VertexMain",
"vs_5_0",
0,
0,
@ptrCast(&vs_blob),
@ptrCast(&error_blob),
);
reportShaderError(.vertex, error_blob);
error_blob = null;
if (hr < 0) {
fatalHr("D3DCompileVertexShader", hr);
}
}
defer _ = vs_blob.IUnknown.Release();
var ps_blob: *win32.ID3DBlob = undefined;
{
const hr = win32.D3DCompile(
shader_source.ptr,
shader_source.len,
null,
null,
null,
"PixelMain",
"ps_5_0",
0,
0,
@ptrCast(&ps_blob),
@ptrCast(&error_blob),
);
if (hr < 0) {
reportShaderError(.pixel, error_blob);
fatalHr("D3DCopmilePixelShader", hr);
}
}
defer _ = ps_blob.IUnknown.Release();
var vertex_shader: *win32.ID3D11VertexShader = undefined;
{
const hr = global.d3d.device.CreateVertexShader(
@ptrCast(vs_blob.GetBufferPointer()),
vs_blob.GetBufferSize(),
null,
&vertex_shader,
);
if (hr < 0) fatalHr("CreateVertexShader", hr);
}
errdefer vertex_shader.IUnknown.Release();
var pixel_shader: *win32.ID3D11PixelShader = undefined;
{
const hr = global.d3d.device.CreatePixelShader(
@ptrCast(ps_blob.GetBufferPointer()),
ps_blob.GetBufferSize(),
null,
&pixel_shader,
);
if (hr < 0) fatalHr("CreatePixelShader", hr);
}
errdefer pixel_shader.IUnknown.Release();
return .{
.vertex = vertex_shader,
.pixel = pixel_shader,
};
}
pub fn deinit(self: *Shaders) void {
_ = self.pixel.IUnknown.Release();
_ = self.vertex.IUnknown.Release();
self.* = undefined;
}
};
fn reportShaderError(kind: enum { vertex, pixel }, maybe_error_blob: ?*win32.ID3DBlob) void {
const err = maybe_error_blob orelse return;
defer _ = err.IUnknown.Release();
const ptr: [*]const u8 = @ptrCast(err.GetBufferPointer() orelse return);
const str = ptr[0..err.GetBufferSize()];
std.log.err("{s} shader error:\n{s}\n", .{ @tagName(kind), str });
}
fn WndProc(
hwnd: win32.HWND,
msg: u32,
wparam: win32.WPARAM,
lparam: win32.LPARAM,
) callconv(std.os.windows.WINAPI) win32.LRESULT {
switch (msg) {
win32.WM_CREATE => {
std.debug.assert(global.state == null);
const swap_chain = initSwapChain(global.d3d.device, hwnd);
global.state = .{
.hwnd = hwnd,
.swap_chain = swap_chain,
.shader_cells = .{},
.glyph_texture = .{
.obj = undefined, // TODO
.view = undefined, // TODO
},
};
std.debug.assert(&(global.state.?) == stateFromHwnd(hwnd));
return 0;
},
win32.WM_DESTROY => @panic("todo"),
win32.WM_CLOSE => {
_ = win32.PostQuitMessage(0);
return 0;
},
win32.WM_DPICHANGED => {
win32.invalidateHwnd(hwnd);
const rect: *win32.RECT = @ptrFromInt(@as(usize, @bitCast(lparam)));
if (0 == win32.SetWindowPos(
hwnd,
null, // ignored via NOZORDER
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
.{ .NOZORDER = 1 },
)) fatalWin32("SetWindowPos", win32.GetLastError());
return 0;
},
win32.WM_PAINT => {
var ps: win32.PAINTSTRUCT = undefined;
_ = win32.BeginPaint(hwnd, &ps) orelse fatalWin32("BeginPaint", win32.GetLastError());
defer if (0 == win32.EndPaint(hwnd, &ps)) fatalWin32("EndPaint", win32.GetLastError());
const dpi = win32.dpiFromHwnd(hwnd);
const state = stateFromHwnd(hwnd);
const client_size = getClientSize(u32, hwnd);
{
const swap_chain_size = getSwapChainSize(state.swap_chain);
if (swap_chain_size.x != client_size.x or swap_chain_size.y != client_size.y) {
if (false) std.log.info(
"SwapChain Buffer Resize from {}x{} to {}x{}",
.{ swap_chain_size.x, swap_chain_size.y, client_size.x, client_size.y },
);
global.d3d.context.ClearState();
if (state.maybe_target_view) |target_view| {
_ = target_view.IUnknown.Release();
state.maybe_target_view = null;
}
global.d3d.context.Flush();
if (swap_chain_size.x == 0) @panic("possible? no need to resize?");
if (swap_chain_size.y == 0) @panic("possible? no need to resize?");
{
const hr = state.swap_chain.IDXGISwapChain.ResizeBuffers(
0,
@intCast(client_size.x),
@intCast(client_size.y),
.UNKNOWN,
swap_chain_flags,
);
if (hr < 0) fatalHr("ResizeBuffers", hr);
}
}
}
const cell_size: XY(u32) = .{
.x = win32.scaleDpi(u32, 11, dpi),
.y = win32.scaleDpi(u32, 18, dpi),
};
const viewport_cell_size: XY(u32) = .{
.x = @divTrunc(client_size.x + cell_size.x - 1, cell_size.x),
.y = @divTrunc(client_size.y + cell_size.y - 1, cell_size.y),
};
{
var mapped: win32.D3D11_MAPPED_SUBRESOURCE = undefined;
const hr = global.d3d.context.Map(
&global.const_buf.ID3D11Resource,
0,
.WRITE_DISCARD,
0,
&mapped,
);
if (hr < 0) fatalHr("MapConstBuffer", hr);
defer global.d3d.context.Unmap(&global.const_buf.ID3D11Resource, 0);
const config: *GridConfig = @ptrCast(@alignCast(mapped.pData));
config.cell_size[0] = @intCast(cell_size.x);
config.cell_size[1] = @intCast(cell_size.y);
config.viewport_cell_size[0] = @intCast(viewport_cell_size.x);
config.viewport_cell_size[1] = @intCast(viewport_cell_size.y);
}
{
const cell_count = viewport_cell_size.x * viewport_cell_size.y;
state.shader_cells.updateCount(cell_count);
}
if (state.shader_cells.count > 0) {
var mapped: win32.D3D11_MAPPED_SUBRESOURCE = undefined;
const hr = global.d3d.context.Map(
&state.shader_cells.cell_buf.ID3D11Resource,
0,
.WRITE_DISCARD,
0,
&mapped,
);
if (hr < 0) fatalHr("MapCellBuffer", hr);
defer global.d3d.context.Unmap(&state.shader_cells.cell_buf.ID3D11Resource, 0);
const cells: [*]Cell = @ptrCast(@alignCast(mapped.pData));
for (0..state.shader_cells.count) |i| {
cells[i] = .{
.glyph_index = global.rand.random().int(u32) % 256,
.background = global.rand.random().int(u32) | 0xff,
.foreground = global.rand.random().int(u32) | 0xff,
};
}
}
if (state.maybe_target_view == null) {
state.maybe_target_view = createRenderTargetView(
global.d3d.device,
&state.swap_chain.IDXGISwapChain,
client_size,
);
}
{
var target_views = [_]?*win32.ID3D11RenderTargetView{state.maybe_target_view.?};
global.d3d.context.OMSetRenderTargets(target_views.len, &target_views, null);
}
global.d3d.context.PSSetConstantBuffers(0, 1, @constCast(@ptrCast(&global.const_buf)));
var resources = [_]?*win32.ID3D11ShaderResourceView{
if (state.shader_cells.count > 0) state.shader_cells.cell_view else null,
//state.glyph_texture.view,
};
global.d3d.context.PSSetShaderResources(0, 1, &resources);
global.d3d.context.VSSetShader(global.shaders.vertex, null, 0);
global.d3d.context.PSSetShader(global.shaders.pixel, null, 0);
global.d3d.context.Draw(4, 0);
// NOTE: don't enable vsync, it causes the gpu to lag behind horribly
// if we flood it with resize events
{
const hr = state.swap_chain.IDXGISwapChain.Present(0, 0);
if (hr < 0) fatalHr("SwapChainPresent", hr);
}
return 0;
},
else => return win32.DefWindowProcW(hwnd, msg, wparam, lparam),
}
}
const ShaderCells = struct {
count: u32 = 0,
cell_buf: *win32.ID3D11Buffer = undefined,
cell_view: *win32.ID3D11ShaderResourceView = undefined,
pub fn updateCount(self: *ShaderCells, count: u32) void {
if (count == self.count) return;
if (false) std.log.info("CellCount {} > {}", .{ self.count, count });
if (self.count != 0) {
_ = self.cell_view.IUnknown.Release();
_ = self.cell_buf.IUnknown.Release();
self.count = 0;
}
if (count > 0) {
self.cell_buf = createCellBuffer(global.d3d.device, count);
errdefer {
self.cell_buf.IUnknown.Release();
self.cell_buf = undefined;
}
{
const desc: win32.D3D11_SHADER_RESOURCE_VIEW_DESC = .{
.Format = .UNKNOWN,
.ViewDimension = ._SRV_DIMENSION_BUFFER,
.Anonymous = .{
.Buffer = .{
.Anonymous1 = .{ .FirstElement = 0 },
.Anonymous2 = .{ .NumElements = count },
},
},
};
const hr = global.d3d.device.CreateShaderResourceView(
&self.cell_buf.ID3D11Resource,
&desc,
&self.cell_view,
);
if (hr < 0) fatalHr("CreateShaderResourceView for cells", hr);
}
}
self.count = count;
}
};
const GlyphTexture = struct {
obj: *win32.ID3D11Texture2D,
view: *win32.ID3D11ShaderResourceView,
};
fn createRenderTargetView(
device: *win32.ID3D11Device,
swap_chain: *win32.IDXGISwapChain,
size: XY(u32),
) *win32.ID3D11RenderTargetView {
var back_buffer: *win32.ID3D11Texture2D = undefined;
{
const hr = swap_chain.GetBuffer(0, win32.IID_ID3D11Texture2D, @ptrCast(&back_buffer));
if (hr < 0) fatalHr("SwapChainGetBuffer", hr);
}
defer _ = back_buffer.IUnknown.Release();
var target_view: *win32.ID3D11RenderTargetView = undefined;
{
const hr = device.CreateRenderTargetView(&back_buffer.ID3D11Resource, null, &target_view);
if (hr < 0) fatalHr("CreateRenderTargetView", hr);
}
{
var viewport = win32.D3D11_VIEWPORT{
.TopLeftX = 0,
.TopLeftY = 0,
.Width = @floatFromInt(size.x),
.Height = @floatFromInt(size.y),
.MinDepth = 0.0,
.MaxDepth = 0.0,
};
global.d3d.context.RSSetViewports(1, @ptrCast(&viewport));
}
// TODO: is this the right place to put this?
global.d3d.context.IASetPrimitiveTopology(._PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
return target_view;
}
fn createCellBuffer(device: *win32.ID3D11Device, count: u32) *win32.ID3D11Buffer {
var cell_buffer: *win32.ID3D11Buffer = undefined;
const buffer_desc: win32.D3D11_BUFFER_DESC = .{
.ByteWidth = count * @sizeOf(Cell),
.Usage = .DYNAMIC,
.BindFlags = .{ .SHADER_RESOURCE = 1 },
.CPUAccessFlags = .{ .WRITE = 1 },
.MiscFlags = .{ .BUFFER_STRUCTURED = 1 },
.StructureByteStride = @sizeOf(Cell),
};
const hr = device.CreateBuffer(&buffer_desc, null, &cell_buffer);
if (hr < 0) fatalHr("CreateCellBuffer", hr);
return cell_buffer;
}
fn getClientSize(comptime T: type, hwnd: win32.HWND) XY(T) {
var rect: win32.RECT = undefined;
if (0 == win32.GetClientRect(hwnd, &rect))
fatalWin32("GetClientRect", win32.GetLastError());
std.debug.assert(rect.left == 0);
std.debug.assert(rect.top == 0);
return .{ .x = @intCast(rect.right), .y = @intCast(rect.bottom) };
}
pub fn XY(comptime T: type) type {
return struct {
x: T,
y: T,
pub fn init(x: T, y: T) @This() {
return .{ .x = x, .y = y };
}
const Self = @This();
pub fn eql(self: Self, other: Self) bool {
return self.x == other.x and self.y == other.y;
}
};
}
threadlocal var thread_is_panicing = false;
pub fn panic(
msg: []const u8,
error_return_trace: ?*std.builtin.StackTrace,
ret_addr: ?usize,
) noreturn {
if (!thread_is_panicing) {
thread_is_panicing = true;
const msg_z: [:0]const u8 = if (std.fmt.allocPrintZ(
std.heap.page_allocator,
"{s}",
.{msg},
)) |msg_z| msg_z else |_| "failed allocate error message";
_ = win32.MessageBoxA(null, msg_z, "Flow Panic", .{ .ICONASTERISK = 1 });
}
std.builtin.default_panic(msg, error_return_trace, ret_addr);
}
fn fatalWin32(what: []const u8, err: win32.WIN32_ERROR) noreturn {
std.debug.panic("{s} failed with {}", .{ what, err.fmt() });
}
fn fatalHr(what: []const u8, hresult: win32.HRESULT) noreturn {
std.debug.panic("{s} failed, hresult=0x{x}", .{ what, @as(u32, @bitCast(hresult)) });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment