Initial commit
This commit is contained in:
commit
32f6e6c14d
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
zig-pkg/
|
||||
.DS_Store
|
||||
*.log
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.pi/npm/
|
||||
33
.pi/agents/algorithm-researcher.md
Normal file
33
.pi/agents/algorithm-researcher.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: algorithm-researcher
|
||||
description: Research chess algorithms, board representation, move generation, search, graphics, and Zig/library behavior with strict citations
|
||||
tools: read, bash, web_search, code_search, fetch_content, get_search_content
|
||||
thinking: high
|
||||
---
|
||||
You are the Algorithm Researcher for zig-chess.
|
||||
|
||||
Mission:
|
||||
- Help the owner learn algorithms for chess rules, board representation, move generation, engine/search experiments, and board rendering.
|
||||
- Do not write implementation code unless explicitly requested. Prefer equations, pseudocode, data-flow descriptions, and learning exercises.
|
||||
- Be highly factual and cite sources for every nontrivial claim.
|
||||
|
||||
Research standards:
|
||||
- Use web_search for current docs, papers, and reputable references when needed.
|
||||
- Prefer primary sources: Zig language docs, Vulkan/GLFW docs, chess rules/standards, engine documentation, textbooks, seminal papers, lecture notes from universities, and well-known technical references.
|
||||
- For each recommendation, include references with title, author/organization, URL/DOI if available, and why it is relevant.
|
||||
- Separate: established result, implementation tradeoff, hypothesis, and suggested experiment.
|
||||
- If sources disagree or are incomplete, say so clearly.
|
||||
|
||||
Focus areas:
|
||||
- Chess rules: legal move generation, check/checkmate/stalemate, castling, en passant, promotion, repetition and draw rules as needed.
|
||||
- Board representation: mailbox arrays, 0x88, bitboards, piece lists, state snapshots.
|
||||
- Testing: perft, FEN fixtures, deterministic regression tests.
|
||||
- Search/evaluation: minimax, negamax, alpha-beta pruning, move ordering, transposition tables, iterative deepening.
|
||||
- Visualization: 2D board rendering, coordinate transforms, highlighting, UI state.
|
||||
|
||||
Output format:
|
||||
1. Short answer / recommendation.
|
||||
2. Concepts and algorithm description.
|
||||
3. Tradeoffs and failure modes.
|
||||
4. Suggested experiments or benchmarks.
|
||||
5. References.
|
||||
32
.pi/agents/optimization-reviewer.md
Normal file
32
.pi/agents/optimization-reviewer.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
name: optimization-reviewer
|
||||
description: Review designs and future Zig code for performance, memory layout, profiling, SIMD/bitboards, rendering latency, and correctness risks with references
|
||||
tools: read, bash, web_search, code_search, fetch_content, get_search_content
|
||||
thinking: high
|
||||
---
|
||||
You are the Optimization Reviewer for zig-chess.
|
||||
|
||||
Mission:
|
||||
- Help the owner understand and improve performance without taking away the learning process.
|
||||
- Do not write implementation code unless explicitly requested. Provide review notes, measurement plans, pseudocode, and targeted explanations.
|
||||
- Ground performance advice in measurable claims and cite references.
|
||||
|
||||
Review standards:
|
||||
- Ask for or propose benchmarks before recommending micro-optimizations.
|
||||
- Distinguish latency, throughput, memory bandwidth, allocation behavior, correctness, determinism, and UI responsiveness.
|
||||
- Cite Zig documentation, compiler/LLVM documentation, CPU/vendor optimization manuals, Vulkan/GLFW documentation, and reliable performance engineering resources.
|
||||
- Call out assumptions about CPU architecture, cache sizes, SIMD availability, GPU/driver behavior, OS/windowing backend, and compiler flags.
|
||||
|
||||
Focus areas:
|
||||
- Zig allocators, slices, error handling costs, comptime, packed/extern structs, vector types, build modes.
|
||||
- Data-oriented design: AoS vs SoA, cache locality, branch prediction, alignment.
|
||||
- Chess performance: board representations, bitboards, move generation, perft, search tree branching, transposition tables.
|
||||
- Rendering performance: frame pacing, CPU/GPU boundaries, command-buffer/resource lifetime, avoiding unnecessary redraw work.
|
||||
- Benchmark quality: deterministic positions, timing methodology, regression tracking.
|
||||
|
||||
Output format:
|
||||
1. Performance risk summary.
|
||||
2. Measurement plan and metrics.
|
||||
3. Optimization options ranked by expected impact/risk.
|
||||
4. Zig-specific considerations.
|
||||
5. References.
|
||||
34
.pi/agents/research-and-review.chain.md
Normal file
34
.pi/agents/research-and-review.chain.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
name: research-and-review
|
||||
description: Research an algorithm, then review performance and learning implications
|
||||
---
|
||||
|
||||
## algorithm-researcher
|
||||
output: algorithm-research.md
|
||||
progress: true
|
||||
|
||||
Research this topic for zig-chess: {task}
|
||||
|
||||
Return a factual, cited algorithm explanation, tradeoffs, and suggested learning experiments. Do not write implementation code.
|
||||
|
||||
## optimization-reviewer
|
||||
reads: algorithm-research.md
|
||||
output: optimization-review.md
|
||||
progress: true
|
||||
|
||||
Review the researched approach from a Zig performance, correctness, and rendering/chess-engine perspective.
|
||||
|
||||
Use the prior research as input: {previous}
|
||||
|
||||
Return measurement plans, optimization risks, and cited references. Do not write implementation code.
|
||||
|
||||
## zig-learning-mentor
|
||||
reads: algorithm-research.md, optimization-review.md
|
||||
output: learning-plan.md
|
||||
progress: true
|
||||
|
||||
Turn the research and optimization review into a learning plan for an experienced Go/Python/C/Rust programmer learning Zig.
|
||||
|
||||
Use prior context: {previous}
|
||||
|
||||
Return exercises and study notes. Do not write implementation code.
|
||||
26
.pi/agents/zig-learning-mentor.md
Normal file
26
.pi/agents/zig-learning-mentor.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
name: zig-learning-mentor
|
||||
description: Explain Zig concepts for an experienced Go/Python/C/Rust programmer while preserving learning ownership
|
||||
tools: read, bash, web_search, code_search, fetch_content, get_search_content
|
||||
thinking: medium
|
||||
---
|
||||
You are the Zig Learning Mentor for zig-chess.
|
||||
|
||||
Mission:
|
||||
- Teach Zig to an experienced programmer who knows Go, Python, some C, and Rust.
|
||||
- Do not write implementation code unless explicitly requested.
|
||||
- Prefer conceptual explanations, small exercises, review prompts, and references to official docs.
|
||||
|
||||
Teaching style:
|
||||
- Use comparisons to Go, Python, C, and Rust when they clarify the idea.
|
||||
- Focus on deep topics: allocators, ownership-by-convention, error unions, comptime, build system, C interop, vectors/SIMD, memory layout, testing, profiling.
|
||||
- Use chess examples when helpful: board arrays, move structs, tagged unions for piece types, bitsets/bitboards, legal-move tests, and deterministic benchmarks.
|
||||
- Ask guiding questions when a design choice would be more educational than receiving a direct answer.
|
||||
- If code is requested, keep it minimal and explain every Zig-specific construct.
|
||||
|
||||
Output format:
|
||||
1. Concept explanation.
|
||||
2. Analogy to known languages.
|
||||
3. Pitfalls and debugging tips.
|
||||
4. Suggested exercise.
|
||||
5. References.
|
||||
10
.pi/settings.json
Normal file
10
.pi/settings.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"packages": [
|
||||
"npm:pi-subagents",
|
||||
"npm:pi-web-access",
|
||||
"npm:@earendil-works/pi-agent-core@0.74.0",
|
||||
"npm:@earendil-works/pi-ai@0.74.0",
|
||||
"npm:@earendil-works/pi-coding-agent@0.74.0",
|
||||
"npm:@earendil-works/pi-tui@0.74.0"
|
||||
]
|
||||
}
|
||||
28
AGENTS.md
Normal file
28
AGENTS.md
Normal file
@ -0,0 +1,28 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Core collaboration rule
|
||||
|
||||
The project owner is learning Zig. Do **not** write implementation code unless the owner explicitly asks for code. Prefer explanations, pseudocode, diagrams in prose, performance reasoning, references, and review comments.
|
||||
|
||||
## Required research standard
|
||||
|
||||
For factual claims about algorithms, chess rules/standards, numerical methods, graphics, Zig behavior, compiler behavior, or performance:
|
||||
|
||||
- Cite primary documentation, papers, books, standards, or reputable technical references where possible.
|
||||
- Distinguish established facts from hypotheses or implementation choices.
|
||||
- Include enough citation detail for follow-up: title, author/organization, URL or DOI when available, and the specific topic/section if useful.
|
||||
- Prefer current upstream documentation for Zig and library/API behavior.
|
||||
- Call out uncertainty and suggest verification experiments or benchmarks.
|
||||
|
||||
## Learning focus
|
||||
|
||||
- Explain Zig concepts in terms of Go/Python/C/Rust analogies when helpful.
|
||||
- Favor small learning tasks and experiments over finished solutions.
|
||||
- When reviewing designs, describe tradeoffs and what evidence would decide between options.
|
||||
- Keep notes and plans in `docs/` unless asked otherwise.
|
||||
|
||||
## Safety and quality
|
||||
|
||||
- Do not add dependencies casually; explain the learning or technical reason first.
|
||||
- Do not introduce opaque frameworks that hide the Zig concepts being studied.
|
||||
- For chess/graphics work, discuss correctness, testability, determinism, latency, profiling, and data-layout tradeoffs.
|
||||
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# zig-chess
|
||||
|
||||
A learning-first Zig project for building chess software while continuing the current Vulkan/GLFW rendering work.
|
||||
|
||||
## Intent
|
||||
|
||||
This repository keeps the current rendering bootstrap, but pivots the long-term learning target to chess. The near-term goal is to get the basic rendering path working; after that, chess rules, board representation, UI, and engine/search work become the main Zig learning vehicle.
|
||||
|
||||
The assistant should support learning with explanations, references, algorithms, tradeoffs, and review feedback; it should not write implementation code unless explicitly asked.
|
||||
|
||||
## Initial milestones
|
||||
|
||||
1. Finish the minimal Vulkan/GLFW rendering bootstrap.
|
||||
2. Render a chessboard with simple 2D primitives.
|
||||
3. Model board coordinates, pieces, moves, turns, castling, en passant, and promotion.
|
||||
4. Add legal move generation and rule validation.
|
||||
5. Build an interactive local UI: select pieces, highlight legal moves, update board state.
|
||||
6. Add chess notation and data helpers: FEN, algebraic notation basics, and PGN later if useful.
|
||||
7. Explore engine concepts: evaluation, minimax/negamax, alpha-beta pruning, move ordering, and perft tests.
|
||||
8. Performance work: board representation tradeoffs, allocation behavior, profiling, and benchmarks.
|
||||
|
||||
## Project-local Pi agents
|
||||
|
||||
This repo includes project agents in `.pi/agents/` and Pi package settings in `.pi/settings.json`:
|
||||
|
||||
- `algorithm-researcher` — factual research on chess algorithms, board representation, move generation, search, graphics, and Zig/library behavior.
|
||||
- `optimization-reviewer` — performance, memory-layout, profiling, SIMD/bitboard, and Zig-specific optimization review.
|
||||
- `zig-learning-mentor` — learning-focused Zig explanations and exercises without writing code for you.
|
||||
|
||||
The agents are configured for web-enabled research via `pi-web-access` tools (`web_search`, `fetch_content`, `get_search_content`) and subagent orchestration via `pi-subagents`.
|
||||
71
build.zig
Normal file
71
build.zig
Normal file
@ -0,0 +1,71 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zig-chess",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
|
||||
const vulkan_headers = b.dependency("vulkan_headers", .{});
|
||||
|
||||
const vulkan = b.dependency("vulkan", .{
|
||||
.registry = vulkan_headers.path("registry/vk.xml"),
|
||||
}).module("vulkan-zig");
|
||||
|
||||
exe.root_module.addImport("vulkan", vulkan);
|
||||
|
||||
const zglfw = b.dependency("zglfw", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.import_vulkan = true,
|
||||
});
|
||||
|
||||
const zglfw_mod = zglfw.module("root");
|
||||
zglfw_mod.addImport("vulkan", vulkan);
|
||||
|
||||
exe.root_module.addImport("zglfw", zglfw_mod);
|
||||
|
||||
if (target.result.os.tag != .emscripten) {
|
||||
exe.root_module.linkLibrary(zglfw.artifact("glfw"));
|
||||
}
|
||||
|
||||
const vert_cmd = b.addSystemCommand(&.{
|
||||
"glslc",
|
||||
"--target-env=vulkan1.2",
|
||||
"-o",
|
||||
});
|
||||
const vert_spv = vert_cmd.addOutputFileArg("square.vert.spv");
|
||||
vert_cmd.addFileArg(b.path("shaders/square.vert"));
|
||||
|
||||
const frag_cmd = b.addSystemCommand(&.{
|
||||
"glslc",
|
||||
"--target-env=vulkan1.2",
|
||||
"-o",
|
||||
});
|
||||
const frag_spv = frag_cmd.addOutputFileArg("square.frag.spv");
|
||||
frag_cmd.addFileArg(b.path("shaders/square.frag"));
|
||||
|
||||
exe.root_module.addAnonymousImport("square_vertex_shader", .{
|
||||
.root_source_file = vert_spv,
|
||||
});
|
||||
|
||||
exe.root_module.addAnonymousImport("square_fragment_shader", .{
|
||||
.root_source_file = frag_spv,
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
const run_step = b.step("run", "Run zig-chess");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
20
build.zig.zon
Normal file
20
build.zig.zon
Normal file
@ -0,0 +1,20 @@
|
||||
.{
|
||||
.name = .zig_chess,
|
||||
.version = "0.16.0",
|
||||
.dependencies = .{
|
||||
.zglfw = .{
|
||||
.url = "git+https://github.com/zig-gamedev/zglfw#51003c105d23db378bb59ce415a387b22f1b0892",
|
||||
.hash = "zglfw-0.10.0-dev-zgVDNIy4IQDJNRy4jrP1As-SZxfJpuWhU1iJ-wBab_VD",
|
||||
},
|
||||
.vulkan = .{
|
||||
.url = "git+https://github.com/Snektron/vulkan-zig#b496a6a561ffbbeb530b0f9ed4e059f88c0723a5",
|
||||
.hash = "vulkan-0.0.0-r7Ytx7N9AwD7IZt5_XNHtkJ4G9qY0pCH-cpcOsbL8wzD",
|
||||
},
|
||||
.vulkan_headers = .{
|
||||
.url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.3.283.tar.gz",
|
||||
.hash = "N-V-__8AAAkkoQGn5z1yoNVrwqZfnYmZp8AZ5CJgoHRMQI0c",
|
||||
},
|
||||
},
|
||||
.paths = .{""},
|
||||
.fingerprint = 0x5cebff35895787ea,
|
||||
}
|
||||
57
docs/learning-roadmap.md
Normal file
57
docs/learning-roadmap.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Learning roadmap
|
||||
|
||||
## Phase 0: Zig environment and language model
|
||||
|
||||
- Install/verify Zig and editor tooling.
|
||||
- Learn `zig build`, tests, allocators, slices, error unions, `defer`, `comptime`, and C interop.
|
||||
- Keep notes on concepts that differ from Go/Rust/C.
|
||||
|
||||
## Phase 1: Rendering bootstrap
|
||||
|
||||
Goal: finish the copied Vulkan/GLFW path well enough to draw and present simple 2D shapes.
|
||||
|
||||
Learning targets:
|
||||
- Vulkan setup order and resource lifetimes.
|
||||
- Swapchain, render pass, framebuffers, command buffers, synchronization.
|
||||
- Coordinate transforms for a 2D board.
|
||||
- Frame timing and redraw strategies.
|
||||
|
||||
## Phase 2: Chess model
|
||||
|
||||
Learning targets:
|
||||
- Board coordinates and piece representation.
|
||||
- Move structs and game-state snapshots.
|
||||
- Rule edge cases: castling, en passant, promotion, check, checkmate, stalemate.
|
||||
- Tests for correctness before UI complexity.
|
||||
|
||||
## Phase 3: Interactive board UI
|
||||
|
||||
Learning targets:
|
||||
- Mapping mouse/window coordinates to squares.
|
||||
- Selection state and legal-move highlighting.
|
||||
- Separating rendering state from game rules.
|
||||
- Debug overlays for learning and testing.
|
||||
|
||||
## Phase 4: Notation and validation
|
||||
|
||||
Learning targets:
|
||||
- FEN import/export.
|
||||
- Algebraic notation basics.
|
||||
- Perft-style move-generation tests.
|
||||
- Golden tests and small fixtures.
|
||||
|
||||
## Phase 5: Engine/search experiments
|
||||
|
||||
Learning targets:
|
||||
- Evaluation functions.
|
||||
- Minimax/negamax and alpha-beta pruning.
|
||||
- Move ordering, transposition tables, and iterative deepening as later topics.
|
||||
- Deterministic benchmarks for search changes.
|
||||
|
||||
## Phase 6: Optimization
|
||||
|
||||
Learning targets:
|
||||
- Profiling before optimizing.
|
||||
- Board representation options: mailbox arrays, 0x88, bitboards.
|
||||
- Allocation-free move generation experiments.
|
||||
- SIMD/vectorization and cache-aware data layouts where evidence supports it.
|
||||
27
docs/research-guidelines.md
Normal file
27
docs/research-guidelines.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Research guidelines
|
||||
|
||||
Use these standards when asking subagents for chess algorithms, rendering, or optimization help.
|
||||
|
||||
## Good prompts
|
||||
|
||||
- "Compare mailbox arrays, 0x88, and bitboards for a beginner-friendly Zig chess engine; cite sources and propose experiments."
|
||||
- "Research legal move generation edge cases and perft testing for chess correctness."
|
||||
- "Review a simple Vulkan chessboard renderer for resource lifetime and data-layout risks."
|
||||
- "Explain alpha-beta pruning and move ordering with references and benchmark ideas."
|
||||
|
||||
## Expected evidence
|
||||
|
||||
- Official Zig documentation for Zig behavior.
|
||||
- Chess programming references, engine documentation, or reputable technical articles for chess algorithms.
|
||||
- Vulkan/GLFW upstream documentation for rendering behavior.
|
||||
- Vendor/compiler documentation for low-level optimization claims.
|
||||
|
||||
## Required distinction
|
||||
|
||||
Every research answer should separate:
|
||||
|
||||
1. Established facts.
|
||||
2. Design tradeoffs.
|
||||
3. Hypotheses requiring measurement.
|
||||
4. Suggested experiments/benchmarks.
|
||||
5. References for follow-up.
|
||||
7
shaders/square.frag
Normal file
7
shaders/square.frag
Normal file
@ -0,0 +1,7 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main() {
|
||||
out_color = vec4(0.1, 0.8, 1.0, 1.0);
|
||||
}
|
||||
7
shaders/square.vert
Normal file
7
shaders/square.vert
Normal file
@ -0,0 +1,7 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 in_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(in_position, 0.0, 1.0);
|
||||
}
|
||||
563
src/main.zig
Normal file
563
src/main.zig
Normal file
@ -0,0 +1,563 @@
|
||||
const std = @import("std");
|
||||
const glfw = @import("zglfw");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const square_vert_spv = @embedFile("square_vertex_shader");
|
||||
const square_frag_spv = @embedFile("square_fragment_shader");
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("zig-chess bootstrap\n", .{});
|
||||
std.debug.print("vertex shader bytes: {}\n", .{square_vert_spv.len});
|
||||
std.debug.print("fragment shader bytes: {}\n", .{square_frag_spv.len});
|
||||
|
||||
try glfw.init();
|
||||
defer glfw.terminate();
|
||||
|
||||
glfw.windowHint(.client_api, .no_api);
|
||||
const window = try glfw.Window.create(
|
||||
800,
|
||||
600,
|
||||
"zig-chess",
|
||||
null,
|
||||
null,
|
||||
);
|
||||
defer window.destroy();
|
||||
|
||||
std.debug.print("GLFW platform: {any}\n", .{glfw.getPlatform()});
|
||||
std.debug.print("Vulkan supported by GLFW: {}\n", .{glfw.isVulkanSupported()});
|
||||
|
||||
const size = window.getSize();
|
||||
const fb_size = window.getFramebufferSize();
|
||||
|
||||
std.debug.print("window size: {}x{}\n", .{ size[0], size[1] });
|
||||
std.debug.print("framebuffer size: {}x{}\n", .{ fb_size[0], fb_size[1] });
|
||||
std.debug.print("window visible attr: {}\n", .{window.getAttribute(.visible)});
|
||||
|
||||
window.show();
|
||||
window.requestAttention();
|
||||
|
||||
const base = vk.BaseWrapper.load(glfw.getInstanceProcAddress);
|
||||
const required_extensions = try glfw.getRequiredInstanceExtensions();
|
||||
|
||||
const app_info = vk.ApplicationInfo{
|
||||
.p_application_name = "zig-chess",
|
||||
.application_version = 1,
|
||||
.p_engine_name = "zig-chess",
|
||||
.engine_version = 1,
|
||||
.api_version = @bitCast(vk.makeApiVersion(0, 1, 2, 0)),
|
||||
};
|
||||
|
||||
const instance_create_info = vk.InstanceCreateInfo{
|
||||
.p_application_info = &app_info,
|
||||
.enabled_extension_count = @intCast(required_extensions.len),
|
||||
.pp_enabled_extension_names = required_extensions.ptr,
|
||||
};
|
||||
|
||||
const instance = try base.createInstance(&instance_create_info, null);
|
||||
|
||||
std.debug.print("required instance extensions:\n", .{});
|
||||
for (required_extensions) |extension| {
|
||||
std.debug.print(" {s}\n", .{extension});
|
||||
}
|
||||
|
||||
std.debug.print("Created Vulkan Instance\n", .{});
|
||||
|
||||
const vki = vk.InstanceWrapper.load(instance, base.dispatch.vkGetInstanceProcAddr.?);
|
||||
defer vki.destroyInstance(instance, null);
|
||||
|
||||
var surface: vk.SurfaceKHR = undefined;
|
||||
try glfw.createWindowSurface(instance, window, null, &surface);
|
||||
defer vki.destroySurfaceKHR(instance, surface, null);
|
||||
std.debug.print("Created Vulkan surface\n", .{});
|
||||
|
||||
const physical_devices = try vki.enumeratePhysicalDevicesAlloc(instance, std.heap.page_allocator);
|
||||
defer std.heap.page_allocator.free(physical_devices);
|
||||
|
||||
std.debug.print("physical devices: {}\n", .{physical_devices.len});
|
||||
|
||||
for (physical_devices, 0..) |physical_device, i| {
|
||||
const props = vki.getPhysicalDeviceProperties(physical_device);
|
||||
std.debug.print("device {}: {s}\n", .{ i, std.mem.sliceTo(&props.device_name, 0) });
|
||||
const queue_families = try vki.getPhysicalDeviceQueueFamilyPropertiesAlloc(
|
||||
physical_device,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
defer std.heap.page_allocator.free(queue_families);
|
||||
|
||||
for (queue_families, 0..) |queue_family, queue_index| {
|
||||
const supports_graphics = queue_family.queue_flags.graphics_bit;
|
||||
const supports_compute = queue_family.queue_flags.compute_bit;
|
||||
const supports_transfer = queue_family.queue_flags.transfer_bit;
|
||||
|
||||
const supports_present = try vki.getPhysicalDeviceSurfaceSupportKHR(
|
||||
physical_device,
|
||||
@intCast(queue_index),
|
||||
surface,
|
||||
);
|
||||
|
||||
std.debug.print(
|
||||
" queue {}: count={}, graphics={}, compute={}, transfer={}, present={}\n",
|
||||
.{
|
||||
queue_index,
|
||||
queue_family.queue_count,
|
||||
supports_graphics,
|
||||
supports_compute,
|
||||
supports_transfer,
|
||||
supports_present,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const selected_physical_device = physical_devices[1];
|
||||
const graphics_queue_family_index: u32 = 0;
|
||||
|
||||
const selected_props = vki.getPhysicalDeviceProperties(selected_physical_device);
|
||||
std.debug.print(
|
||||
"selected device: {s}, queue family {}\n",
|
||||
.{
|
||||
std.mem.sliceTo(&selected_props.device_name, 0),
|
||||
graphics_queue_family_index,
|
||||
},
|
||||
);
|
||||
|
||||
const queue_priority: f32 = 1.0;
|
||||
|
||||
const queue_create_info = vk.DeviceQueueCreateInfo{
|
||||
.queue_family_index = graphics_queue_family_index,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = @ptrCast(&queue_priority),
|
||||
};
|
||||
|
||||
const device_extensions = [_][*:0]const u8{
|
||||
"VK_KHR_swapchain",
|
||||
};
|
||||
|
||||
const device_create_info = vk.DeviceCreateInfo{
|
||||
.queue_create_info_count = 1,
|
||||
.p_queue_create_infos = @ptrCast(&queue_create_info),
|
||||
.enabled_extension_count = device_extensions.len,
|
||||
.pp_enabled_extension_names = &device_extensions,
|
||||
};
|
||||
|
||||
const device = try vki.createDevice(selected_physical_device, &device_create_info, null);
|
||||
std.debug.print("created logical device\n", .{});
|
||||
|
||||
const vkd = vk.DeviceWrapper.load(device, vki.dispatch.vkGetDeviceProcAddr.?);
|
||||
defer vkd.destroyDevice(device, null);
|
||||
|
||||
const graphics_queue = vkd.getDeviceQueue(device, graphics_queue_family_index, 0);
|
||||
|
||||
std.debug.print("retrieved graphics queue\n", .{});
|
||||
|
||||
const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
selected_physical_device,
|
||||
surface,
|
||||
);
|
||||
|
||||
std.debug.print(
|
||||
"surface current extent: {}x{}\n",
|
||||
.{
|
||||
surface_caps.current_extent.width,
|
||||
surface_caps.current_extent.height,
|
||||
},
|
||||
);
|
||||
|
||||
std.debug.print(
|
||||
"surface min/max image count: {}/{}\n",
|
||||
.{
|
||||
surface_caps.min_image_count,
|
||||
surface_caps.max_image_count,
|
||||
},
|
||||
);
|
||||
|
||||
const surface_formats = try vki.getPhysicalDeviceSurfaceFormatsAllocKHR(
|
||||
selected_physical_device,
|
||||
surface,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
defer std.heap.page_allocator.free(surface_formats);
|
||||
|
||||
std.debug.print("surface formats: {}\n", .{surface_formats.len});
|
||||
for (surface_formats, 0..) |format, i| {
|
||||
std.debug.print(
|
||||
" format {}: format={any}, color_space={any}\n",
|
||||
.{ i, format.format, format.color_space },
|
||||
);
|
||||
}
|
||||
|
||||
const present_modes = try vki.getPhysicalDeviceSurfacePresentModesAllocKHR(
|
||||
selected_physical_device,
|
||||
surface,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
defer std.heap.page_allocator.free(present_modes);
|
||||
|
||||
std.debug.print("present modes: {}\n", .{present_modes.len});
|
||||
for (present_modes, 0..) |mode, i| {
|
||||
std.debug.print(" present mode {}: {any}\n", .{ i, mode });
|
||||
}
|
||||
|
||||
var chosen_surface_format = surface_formats[0];
|
||||
for (surface_formats) |format| {
|
||||
if (format.format == .b8g8r8a8_srgb and
|
||||
format.color_space == .srgb_nonlinear_khr)
|
||||
{
|
||||
chosen_surface_format = format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var chosen_present_mode: vk.PresentModeKHR = .fifo_khr;
|
||||
for (present_modes) |mode| {
|
||||
if (mode == .fifo_khr) {
|
||||
chosen_present_mode = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const framebuffer_size = window.getFramebufferSize();
|
||||
|
||||
const chosen_extent = if (surface_caps.current_extent.width != std.math.maxInt(u32))
|
||||
surface_caps.current_extent
|
||||
else
|
||||
vk.Extent2D{
|
||||
.width = @intCast(framebuffer_size[0]),
|
||||
.height = @intCast(framebuffer_size[1]),
|
||||
};
|
||||
|
||||
var chosen_image_count = surface_caps.min_image_count + 1;
|
||||
if (surface_caps.max_image_count != 0 and chosen_image_count > surface_caps.max_image_count) {
|
||||
chosen_image_count = surface_caps.max_image_count;
|
||||
}
|
||||
|
||||
std.debug.print(
|
||||
"chosen swapchain format={any}, color_space={any}\n",
|
||||
.{ chosen_surface_format.format, chosen_surface_format.color_space },
|
||||
);
|
||||
std.debug.print("chosen present mode={any}\n", .{chosen_present_mode});
|
||||
std.debug.print(
|
||||
"chosen extent={}x{}\n",
|
||||
.{ chosen_extent.width, chosen_extent.height },
|
||||
);
|
||||
std.debug.print("chosen image count={}\n", .{chosen_image_count});
|
||||
|
||||
const swapchain_create_info = vk.SwapchainCreateInfoKHR{
|
||||
.surface = surface,
|
||||
.min_image_count = chosen_image_count,
|
||||
.image_format = chosen_surface_format.format,
|
||||
.image_color_space = chosen_surface_format.color_space,
|
||||
.image_extent = chosen_extent,
|
||||
.image_array_layers = 1,
|
||||
.image_usage = .{
|
||||
.color_attachment_bit = true,
|
||||
},
|
||||
.image_sharing_mode = .exclusive,
|
||||
.pre_transform = surface_caps.current_transform,
|
||||
.composite_alpha = .{
|
||||
.opaque_bit_khr = true,
|
||||
},
|
||||
.present_mode = chosen_present_mode,
|
||||
.clipped = .true,
|
||||
};
|
||||
|
||||
const swapchain = try vkd.createSwapchainKHR(device, &swapchain_create_info, null);
|
||||
defer vkd.destroySwapchainKHR(device, swapchain, null);
|
||||
|
||||
std.debug.print("created swapchain\n", .{});
|
||||
|
||||
const swapchain_images = try vkd.getSwapchainImagesAllocKHR(
|
||||
device,
|
||||
swapchain,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
defer std.heap.page_allocator.free(swapchain_images);
|
||||
|
||||
std.debug.print("swapchain images: {}\n", .{swapchain_images.len});
|
||||
|
||||
const swapchain_image_views = try std.heap.page_allocator.alloc(
|
||||
vk.ImageView,
|
||||
swapchain_images.len,
|
||||
);
|
||||
defer std.heap.page_allocator.free(swapchain_image_views);
|
||||
|
||||
for (swapchain_images, 0..) |image, i| {
|
||||
const image_view_create_info = vk.ImageViewCreateInfo{
|
||||
.image = image,
|
||||
.view_type = .@"2d",
|
||||
.format = chosen_surface_format.format,
|
||||
.components = .{
|
||||
.r = .identity,
|
||||
.g = .identity,
|
||||
.b = .identity,
|
||||
.a = .identity,
|
||||
},
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{
|
||||
.color_bit = true,
|
||||
},
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
};
|
||||
|
||||
swapchain_image_views[i] = try vkd.createImageView(
|
||||
device,
|
||||
&image_view_create_info,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
defer {
|
||||
for (swapchain_image_views) |image_view| {
|
||||
vkd.destroyImageView(device, image_view, null);
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("created swapchain image views: {}\n", .{swapchain_image_views.len});
|
||||
|
||||
const color_attachment = vk.AttachmentDescription{
|
||||
.format = chosen_surface_format.format,
|
||||
.samples = .{ .@"1_bit" = true },
|
||||
.load_op = .clear,
|
||||
.store_op = .store,
|
||||
.stencil_load_op = .dont_care,
|
||||
.stencil_store_op = .dont_care,
|
||||
.initial_layout = .undefined,
|
||||
.final_layout = .present_src_khr,
|
||||
};
|
||||
|
||||
const color_attachment_ref = vk.AttachmentReference{
|
||||
.attachment = 0,
|
||||
.layout = .color_attachment_optimal,
|
||||
};
|
||||
|
||||
const subpass = vk.SubpassDescription{
|
||||
.pipeline_bind_point = .graphics,
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachments = @ptrCast(&color_attachment_ref),
|
||||
};
|
||||
|
||||
const subpass_dependency = vk.SubpassDependency{
|
||||
.src_subpass = vk.SUBPASS_EXTERNAL,
|
||||
.dst_subpass = 0,
|
||||
.src_stage_mask = .{
|
||||
.color_attachment_output_bit = true,
|
||||
},
|
||||
.src_access_mask = .{},
|
||||
.dst_stage_mask = .{
|
||||
.color_attachment_output_bit = true,
|
||||
},
|
||||
.dst_access_mask = .{
|
||||
.color_attachment_write_bit = true,
|
||||
},
|
||||
};
|
||||
|
||||
const render_pass_create_info = vk.RenderPassCreateInfo{
|
||||
.attachment_count = 1,
|
||||
.p_attachments = @ptrCast(&color_attachment),
|
||||
.subpass_count = 1,
|
||||
.p_subpasses = @ptrCast(&subpass),
|
||||
.dependency_count = 1,
|
||||
.p_dependencies = @ptrCast(&subpass_dependency),
|
||||
};
|
||||
|
||||
const render_pass = try vkd.createRenderPass(device, &render_pass_create_info, null);
|
||||
defer vkd.destroyRenderPass(device, render_pass, null);
|
||||
|
||||
std.debug.print("created render pass\n", .{});
|
||||
|
||||
const framebuffers = try std.heap.page_allocator.alloc(
|
||||
vk.Framebuffer,
|
||||
swapchain_image_views.len,
|
||||
);
|
||||
defer std.heap.page_allocator.free(framebuffers);
|
||||
|
||||
for (swapchain_image_views, 0..) |image_view, i| {
|
||||
const attachments = [_]vk.ImageView{image_view};
|
||||
|
||||
const framebuffer_create_info = vk.FramebufferCreateInfo{
|
||||
.render_pass = render_pass,
|
||||
.attachment_count = attachments.len,
|
||||
.p_attachments = &attachments,
|
||||
.width = chosen_extent.width,
|
||||
.height = chosen_extent.height,
|
||||
.layers = 1,
|
||||
};
|
||||
|
||||
framebuffers[i] = try vkd.createFramebuffer(
|
||||
device,
|
||||
&framebuffer_create_info,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
defer {
|
||||
for (framebuffers) |framebuffer| {
|
||||
vkd.destroyFramebuffer(device, framebuffer, null);
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("created framebuffers: {}\n", .{framebuffers.len});
|
||||
|
||||
const command_pool_create_info = vk.CommandPoolCreateInfo{
|
||||
.flags = .{
|
||||
.reset_command_buffer_bit = true,
|
||||
},
|
||||
.queue_family_index = graphics_queue_family_index,
|
||||
};
|
||||
|
||||
const command_pool = try vkd.createCommandPool(
|
||||
device,
|
||||
&command_pool_create_info,
|
||||
null,
|
||||
);
|
||||
defer vkd.destroyCommandPool(device, command_pool, null);
|
||||
|
||||
std.debug.print("created command pool\n", .{});
|
||||
|
||||
const command_buffers = try std.heap.page_allocator.alloc(
|
||||
vk.CommandBuffer,
|
||||
framebuffers.len,
|
||||
);
|
||||
defer std.heap.page_allocator.free(command_buffers);
|
||||
|
||||
const command_buffer_allocate_info = vk.CommandBufferAllocateInfo{
|
||||
.command_pool = command_pool,
|
||||
.level = .primary,
|
||||
.command_buffer_count = @intCast(command_buffers.len),
|
||||
};
|
||||
|
||||
try vkd.allocateCommandBuffers(
|
||||
device,
|
||||
&command_buffer_allocate_info,
|
||||
command_buffers.ptr,
|
||||
);
|
||||
|
||||
std.debug.print("allocated command buffers: {}\n", .{command_buffers.len});
|
||||
|
||||
for (command_buffers, 0..) |command_buffer, i| {
|
||||
const begin_info = vk.CommandBufferBeginInfo{};
|
||||
|
||||
try vkd.beginCommandBuffer(command_buffer, &begin_info);
|
||||
|
||||
const clear_color = vk.ClearValue{
|
||||
.color = .{
|
||||
.float_32 = .{ 0.02, 0.02, 0.08, 1.0 },
|
||||
},
|
||||
};
|
||||
|
||||
const render_pass_begin_info = vk.RenderPassBeginInfo{
|
||||
.render_pass = render_pass,
|
||||
.framebuffer = framebuffers[i],
|
||||
.render_area = .{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = chosen_extent,
|
||||
},
|
||||
.clear_value_count = 1,
|
||||
.p_clear_values = @ptrCast(&clear_color),
|
||||
};
|
||||
|
||||
vkd.cmdBeginRenderPass(
|
||||
command_buffer,
|
||||
&render_pass_begin_info,
|
||||
.@"inline",
|
||||
);
|
||||
|
||||
vkd.cmdEndRenderPass(command_buffer);
|
||||
|
||||
try vkd.endCommandBuffer(command_buffer);
|
||||
}
|
||||
|
||||
std.debug.print("recorded command buffers\n", .{});
|
||||
|
||||
const semaphore_create_info = vk.SemaphoreCreateInfo{};
|
||||
|
||||
const image_available_semaphore = try vkd.createSemaphore(
|
||||
device,
|
||||
&semaphore_create_info,
|
||||
null,
|
||||
);
|
||||
defer vkd.destroySemaphore(device, image_available_semaphore, null);
|
||||
|
||||
const render_finished_semaphore = try vkd.createSemaphore(
|
||||
device,
|
||||
&semaphore_create_info,
|
||||
null,
|
||||
);
|
||||
defer vkd.destroySemaphore(device, render_finished_semaphore, null);
|
||||
|
||||
const fence_create_info = vk.FenceCreateInfo{
|
||||
.flags = .{
|
||||
.signaled_bit = true,
|
||||
},
|
||||
};
|
||||
|
||||
const in_flight_fence = try vkd.createFence(
|
||||
device,
|
||||
&fence_create_info,
|
||||
null,
|
||||
);
|
||||
defer vkd.destroyFence(device, in_flight_fence, null);
|
||||
|
||||
std.debug.print("created synchronization objects\n", .{});
|
||||
|
||||
const wait_fences = [_]vk.Fence{in_flight_fence};
|
||||
_ = try vkd.waitForFences(device, &wait_fences, .true, std.math.maxInt(u64));
|
||||
try vkd.resetFences(device, &wait_fences);
|
||||
|
||||
const acquire_result = try vkd.acquireNextImageKHR(
|
||||
device,
|
||||
swapchain,
|
||||
std.math.maxInt(u64),
|
||||
image_available_semaphore,
|
||||
.null_handle,
|
||||
);
|
||||
|
||||
const image_index = acquire_result.image_index;
|
||||
std.debug.print("acquired swapchain image: {}\n", .{image_index});
|
||||
|
||||
const wait_semaphores = [_]vk.Semaphore{image_available_semaphore};
|
||||
const wait_stages = [_]vk.PipelineStageFlags{
|
||||
.{
|
||||
.color_attachment_output_bit = true,
|
||||
},
|
||||
};
|
||||
const signal_semaphores = [_]vk.Semaphore{render_finished_semaphore};
|
||||
const submit_command_buffers = [_]vk.CommandBuffer{
|
||||
command_buffers[image_index],
|
||||
};
|
||||
|
||||
const submit_info = vk.SubmitInfo{
|
||||
.wait_semaphore_count = wait_semaphores.len,
|
||||
.p_wait_semaphores = &wait_semaphores,
|
||||
.p_wait_dst_stage_mask = &wait_stages,
|
||||
.command_buffer_count = submit_command_buffers.len,
|
||||
.p_command_buffers = &submit_command_buffers,
|
||||
.signal_semaphore_count = signal_semaphores.len,
|
||||
.p_signal_semaphores = &signal_semaphores,
|
||||
};
|
||||
|
||||
try vkd.queueSubmit(graphics_queue, &[_]vk.SubmitInfo{submit_info}, in_flight_fence);
|
||||
|
||||
const present_swapchains = [_]vk.SwapchainKHR{swapchain};
|
||||
const present_image_indices = [_]u32{image_index};
|
||||
|
||||
const present_info = vk.PresentInfoKHR{
|
||||
.wait_semaphore_count = signal_semaphores.len,
|
||||
.p_wait_semaphores = &signal_semaphores,
|
||||
.swapchain_count = present_swapchains.len,
|
||||
.p_swapchains = &present_swapchains,
|
||||
.p_image_indices = &present_image_indices,
|
||||
};
|
||||
|
||||
_ = try vkd.queuePresentKHR(graphics_queue, &present_info);
|
||||
|
||||
std.debug.print("presented one frame\n", .{});
|
||||
|
||||
while (!window.shouldClose()) {
|
||||
glfw.pollEvents();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user