jai port :D
This commit is contained in:
BIN
.build/autosample.exp
Normal file
BIN
.build/autosample.exp
Normal file
Binary file not shown.
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Read(//c/opt/jai/**)",
|
||||||
|
"Bash(cd:*)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
30
.vscode/tasks.json
vendored
30
.vscode/tasks.json
vendored
@@ -1,40 +1,36 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
|
||||||
"label": "bootstrap nob",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "cl",
|
|
||||||
"args": ["/nologo", "nob.c"],
|
|
||||||
"group": "build",
|
|
||||||
"problemMatcher": "$msCompile",
|
|
||||||
"detail": "One-time bootstrap: compile nob.c with cl.exe"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "build",
|
"label": "build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/nob.exe",
|
"command": "jai",
|
||||||
"group": "build",
|
"args": ["build.jai"],
|
||||||
"problemMatcher": "$msCompile",
|
"group": {
|
||||||
"detail": "Build autosample via nob"
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"detail": "Build autosample via jai"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "rebuild",
|
"label": "rebuild",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/nob.exe",
|
"command": "jai",
|
||||||
|
"args": ["build.jai"],
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"problemMatcher": "$msCompile",
|
"problemMatcher": [],
|
||||||
"detail": "Clean and rebuild",
|
"detail": "Clean and rebuild",
|
||||||
"dependsOn": "clean"
|
"dependsOn": "clean"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "clean",
|
"label": "clean",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "if (Test-Path build) { Remove-Item -Recurse -Force build }",
|
"command": "if (Test-Path build) { Remove-Item -Recurse -Force build }; if (Test-Path .build) { Remove-Item -Recurse -Force .build }",
|
||||||
"args": [],
|
"args": [],
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"detail": "Remove build directory"
|
"detail": "Remove build and .build directories"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
71
build.jai
Normal file
71
build.jai
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#import "Basic";
|
||||||
|
#import "Compiler";
|
||||||
|
#import "File";
|
||||||
|
#import "File_Utilities";
|
||||||
|
|
||||||
|
IMGUI_PATH :: "modules/ImGui/src";
|
||||||
|
IMGUI_LIB :: "modules/ImGui/windows/ImGui.lib";
|
||||||
|
|
||||||
|
build :: () {
|
||||||
|
set_build_options_dc(.{do_output=false});
|
||||||
|
|
||||||
|
// Build ImGui from source if the static lib doesn't exist
|
||||||
|
if !file_exists(IMGUI_LIB) {
|
||||||
|
print("ImGui.lib not found — building from source...\n");
|
||||||
|
make_directory_if_it_does_not_exist("modules/ImGui/windows");
|
||||||
|
|
||||||
|
success := build_cpp_static_lib("modules/ImGui/windows/ImGui",
|
||||||
|
tprint("%/imgui.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/imgui_widgets.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/imgui_draw.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/imgui_tables.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/imgui_demo.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/backends/imgui_impl_win32.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/backends/imgui_impl_dx12.cpp", IMGUI_PATH),
|
||||||
|
tprint("%/imgui_backend_c.cpp", IMGUI_PATH),
|
||||||
|
extra = .[
|
||||||
|
"/DIMGUI_API=__declspec(dllexport)",
|
||||||
|
"/DIMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS",
|
||||||
|
tprint("/I%", IMGUI_PATH),
|
||||||
|
tprint("/I%/backends", IMGUI_PATH),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
compiler_set_workspace_status(.FAILED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print("ImGui.lib built successfully.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
w := compiler_create_workspace("autosample");
|
||||||
|
if !w {
|
||||||
|
print("Workspace creation failed.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options := get_build_options(w);
|
||||||
|
options.output_executable_name = "autosample";
|
||||||
|
options.output_path = "build/";
|
||||||
|
|
||||||
|
import_path: [..] string;
|
||||||
|
array_add(*import_path, "modules"); // Our local modules (ImGui)
|
||||||
|
for options.import_path array_add(*import_path, it);
|
||||||
|
options.import_path = import_path;
|
||||||
|
|
||||||
|
set_build_options(options, w);
|
||||||
|
compiler_begin_intercept(w);
|
||||||
|
add_build_file("src/main.jai", w);
|
||||||
|
|
||||||
|
while true {
|
||||||
|
message := compiler_wait_for_message();
|
||||||
|
if !message break;
|
||||||
|
if message.kind == .COMPLETE break;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler_end_intercept(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
#run build();
|
||||||
|
|
||||||
|
#import "BuildCpp";
|
||||||
56
modules/ImGui/.build/.added_strings_w2.jai
Normal file
56
modules/ImGui/.build/.added_strings_w2.jai
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Workspace: Target Program
|
||||||
|
|
||||||
|
//
|
||||||
|
// String added via add_build_string() from c:/Users/mta/.vscode-oss/extensions/apparentlystudio.jails-0.2.0-universal/out/metaprogram/jails_diagnostics.jai:19.
|
||||||
|
//
|
||||||
|
JAILS_DIAGNOSTICS_BUILD :: true;
|
||||||
|
|
||||||
|
//
|
||||||
|
// #insert text. Generated from C:/opt/jai/modules/Bindings_Generator/module.jai:369.
|
||||||
|
//
|
||||||
|
([2] string).[
|
||||||
|
.["FLT_MIN", "FLOAT32_MIN"],
|
||||||
|
.["FLT_MAX", "FLOAT32_MAX"],
|
||||||
|
.["DBL_MIN", "FLOAT64_MIN"],
|
||||||
|
.["DBL_MAX", "FLOAT64_MAX"],
|
||||||
|
|
||||||
|
.["SCHAR_MIN", "S8_MIN"],
|
||||||
|
.["SCHAR_MAX", "S8_MAX"],
|
||||||
|
.["UCHAR_MIN", "0"],
|
||||||
|
.["UCHAR_MAX", "U8_MAX"],
|
||||||
|
|
||||||
|
.["SHRT_MIN", "S16_MIN"],
|
||||||
|
.["SHRT_MAX", "S16_MAX"],
|
||||||
|
.["USHRT_MIN", "0"],
|
||||||
|
.["USHRT_MAX", "U16_MAX"],
|
||||||
|
|
||||||
|
.["INT_MIN", "S32_MIN"],
|
||||||
|
.["INT_MAX", "S32_MAX"],
|
||||||
|
.["UINT_MIN", "0"],
|
||||||
|
.["UINT_MAX", "U32_MAX"],
|
||||||
|
|
||||||
|
.["LLONG_MIN", "S64_MIN"],
|
||||||
|
.["LLONG_MAX", "S64_MAX"],
|
||||||
|
.["ULLONG_MIN", "0"],
|
||||||
|
.["ULLONG_MAX", "U64_MAX"],
|
||||||
|
|
||||||
|
.["INT8_MIN", "S8_MIN"],
|
||||||
|
.["INT8_MAX", "S8_MAX"],
|
||||||
|
.["UINT8_MAX", "U8_MAX"],
|
||||||
|
|
||||||
|
.["INT16_MIN", "S16_MIN"],
|
||||||
|
.["INT16_MAX", "S16_MAX"],
|
||||||
|
.["UINT16_MAX", "U16_MAX"],
|
||||||
|
|
||||||
|
.["INT32_MIN", "S32_MIN"],
|
||||||
|
.["INT32_MAX", "S32_MAX"],
|
||||||
|
.["UINT32_MAX", "U32_MAX"],
|
||||||
|
|
||||||
|
.["INT64_MIN", "S64_MIN"],
|
||||||
|
.["INT64_MAX", "S64_MAX"],
|
||||||
|
.["UINT64_MAX", "U64_MAX"],
|
||||||
|
.["LONG_MIN", "S32_MIN"],
|
||||||
|
.["LONG_MAX", "S32_MAX"],
|
||||||
|
.["ULONG_MIN", "0"],
|
||||||
|
.["ULONG_MAX", "U32_MAX"],
|
||||||
|
];
|
||||||
47
modules/ImGui/backend.jai
Normal file
47
modules/ImGui/backend.jai
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Backend and DockBuilder declarations.
|
||||||
|
// These call into extern "C" wrappers in imgui_backend_c.cpp, compiled into ImGui.lib.
|
||||||
|
|
||||||
|
#scope_export
|
||||||
|
|
||||||
|
d3d12 :: #import "d3d12";
|
||||||
|
dxgi :: #import "dxgi";
|
||||||
|
Windows :: #import "Windows";
|
||||||
|
|
||||||
|
// ImGui_ImplDX12_InitInfo — mirrors the C++ struct layout.
|
||||||
|
ImplDX12_InitInfo :: struct {
|
||||||
|
Device: *d3d12.ID3D12Device;
|
||||||
|
CommandQueue: *d3d12.ID3D12CommandQueue;
|
||||||
|
NumFramesInFlight: s32;
|
||||||
|
RTVFormat: dxgi.DXGI_FORMAT;
|
||||||
|
DSVFormat: dxgi.DXGI_FORMAT;
|
||||||
|
UserData: *void;
|
||||||
|
SrvDescriptorHeap: *d3d12.ID3D12DescriptorHeap;
|
||||||
|
SrvDescriptorAllocFn: #type (info: *ImplDX12_InitInfo, out_cpu: *d3d12.D3D12_CPU_DESCRIPTOR_HANDLE, out_gpu: *d3d12.D3D12_GPU_DESCRIPTOR_HANDLE) #c_call;
|
||||||
|
SrvDescriptorFreeFn: #type (info: *ImplDX12_InitInfo, cpu: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE, gpu: d3d12.D3D12_GPU_DESCRIPTOR_HANDLE) #c_call;
|
||||||
|
// Legacy single-descriptor fields (IMGUI_DISABLE_OBSOLETE_FUNCTIONS not set)
|
||||||
|
LegacySingleSrvCpuDescriptor: d3d12.D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||||
|
LegacySingleSrvGpuDescriptor: d3d12.D3D12_GPU_DESCRIPTOR_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32 backend
|
||||||
|
ImplWin32_Init :: (hwnd: *void) -> bool #foreign imgui "jai_ImGui_ImplWin32_Init";
|
||||||
|
ImplWin32_Shutdown :: () #foreign imgui "jai_ImGui_ImplWin32_Shutdown";
|
||||||
|
ImplWin32_NewFrame :: () #foreign imgui "jai_ImGui_ImplWin32_NewFrame";
|
||||||
|
ImplWin32_WndProcHandler :: (hWnd: Windows.HWND, msg: u32, wParam: Windows.WPARAM, lParam: Windows.LPARAM) -> Windows.LRESULT #foreign imgui "jai_ImGui_ImplWin32_WndProcHandler";
|
||||||
|
|
||||||
|
// DX12 backend
|
||||||
|
ImplDX12_Init :: (info: *ImplDX12_InitInfo) -> bool #foreign imgui "jai_ImGui_ImplDX12_Init";
|
||||||
|
ImplDX12_Shutdown :: () #foreign imgui "jai_ImGui_ImplDX12_Shutdown";
|
||||||
|
ImplDX12_NewFrame :: () #foreign imgui "jai_ImGui_ImplDX12_NewFrame";
|
||||||
|
ImplDX12_RenderDrawData :: (draw_data: *ImDrawData, cmd_list: *d3d12.ID3D12GraphicsCommandList) #foreign imgui "jai_ImGui_ImplDX12_RenderDrawData";
|
||||||
|
|
||||||
|
// DockBuilder (from imgui_internal.h)
|
||||||
|
DockBuilderRemoveNode :: (node_id: ID) #foreign imgui "jai_DockBuilderRemoveNode";
|
||||||
|
DockBuilderAddNode :: (node_id: ID, flags: s32) #foreign imgui "jai_DockBuilderAddNode";
|
||||||
|
DockBuilderSetNodeSize :: (node_id: ID, size: ImVec2) #foreign imgui "jai_DockBuilderSetNodeSize";
|
||||||
|
DockBuilderSplitNode :: (node_id: ID, split_dir: s32, size_ratio: float32, out_id_at_dir: *ID, out_id_at_opposite_dir: *ID) -> ID #foreign imgui "jai_DockBuilderSplitNode";
|
||||||
|
DockBuilderDockWindow :: (window_name: *u8, node_id: ID) #foreign imgui "jai_DockBuilderDockWindow";
|
||||||
|
DockBuilderFinish :: (node_id: ID) #foreign imgui "jai_DockBuilderFinish";
|
||||||
|
|
||||||
|
// Font loading
|
||||||
|
AddFontFromFileTTF :: (filename: *u8, size_pixels: float32) -> *void #foreign imgui "jai_AddFontFromFileTTF";
|
||||||
239
modules/ImGui/generate.jai
Normal file
239
modules/ImGui/generate.jai
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
AT_COMPILE_TIME :: true;
|
||||||
|
|
||||||
|
IMGUI_PATH :: "src";
|
||||||
|
|
||||||
|
DECLARATIONS_TO_OMIT :: string.[
|
||||||
|
"ImVec2",
|
||||||
|
"ImVec4",
|
||||||
|
"ImColor",
|
||||||
|
"ImVector",
|
||||||
|
"ImDrawCallback",
|
||||||
|
"ImGuiTextBuffer",
|
||||||
|
"ImDrawCallback_ResetRenderState",
|
||||||
|
];
|
||||||
|
|
||||||
|
#if AT_COMPILE_TIME {
|
||||||
|
#run,stallable {
|
||||||
|
set_build_options_dc(.{do_output=false});
|
||||||
|
root_options := get_build_options();
|
||||||
|
args := root_options.compile_time_command_line;
|
||||||
|
if !generate_bindings(args) {
|
||||||
|
compiler_set_workspace_status(.FAILED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#import "System";
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
set_working_directory(path_strip_filename(get_path_of_running_executable()));
|
||||||
|
args := get_command_line_arguments();
|
||||||
|
if !generate_bindings(args) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_bindings :: (args: [] string) -> bool {
|
||||||
|
compile := array_find(args, "-compile");
|
||||||
|
compile_debug := array_find(args, "-debug");
|
||||||
|
|
||||||
|
if compile {
|
||||||
|
imgui_src_files: [..] string;
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui_widgets.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui_draw.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui_tables.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui_demo.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/backends/imgui_impl_win32.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/backends/imgui_impl_dx12.cpp", IMGUI_PATH));
|
||||||
|
array_add(*imgui_src_files, tprint("%/imgui_backend_c.cpp", IMGUI_PATH));
|
||||||
|
|
||||||
|
success := true;
|
||||||
|
#if OS == .WINDOWS {
|
||||||
|
make_directory_if_it_does_not_exist("windows");
|
||||||
|
success &&= build_cpp_static_lib("windows/ImGui", ..imgui_src_files,
|
||||||
|
extra = .[
|
||||||
|
"/DIMGUI_API=__declspec(dllexport)",
|
||||||
|
"/DIMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS",
|
||||||
|
tprint("/I%", IMGUI_PATH),
|
||||||
|
tprint("/I%/backends", IMGUI_PATH),
|
||||||
|
],
|
||||||
|
debug=compile_debug);
|
||||||
|
} else {
|
||||||
|
assert(false, "Only Windows is supported for this project.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !success return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defer array_reset(*begin_end_string_args);
|
||||||
|
|
||||||
|
output_filename: string;
|
||||||
|
opts: Generate_Bindings_Options;
|
||||||
|
{
|
||||||
|
using opts;
|
||||||
|
|
||||||
|
#if OS == .WINDOWS {
|
||||||
|
array_add(*library_search_paths, "windows");
|
||||||
|
output_filename = "windows.jai";
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_add(*libraries, .{filename="ImGui"});
|
||||||
|
array_add(*include_paths, IMGUI_PATH);
|
||||||
|
array_add(*source_files, tprint("%/imgui.h", IMGUI_PATH));
|
||||||
|
array_add(*extra_clang_arguments, "-x", "c++", "-DWIN32_LEAN_AND_MEAN");
|
||||||
|
array_add(*flatten_namespaces, "ImGui");
|
||||||
|
array_add(*strip_prefixes, "ImGui");
|
||||||
|
auto_detect_enum_prefixes = false;
|
||||||
|
log_stripped_declarations = false;
|
||||||
|
generate_compile_time_struct_checks = false;
|
||||||
|
strip_flags |= .INLINED_FUNCTIONS;
|
||||||
|
|
||||||
|
visitor = imgui_visitor;
|
||||||
|
get_func_args_for_printing = imgui_get_function_arg_for_printing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return generate_bindings(opts, output_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_end_string_args: [..]*Declaration;
|
||||||
|
|
||||||
|
enums_for_typedefs: Table(string, *Enum);
|
||||||
|
|
||||||
|
imgui_visitor :: (decl: *Declaration, parent_decl: *Declaration) -> Declaration_Visit_Result {
|
||||||
|
get_associated_enum_name :: (name: string) -> string {
|
||||||
|
if name.count > 1 && name[name.count - 1] != #char "_" {
|
||||||
|
return tprint("%_", name);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !parent_decl {
|
||||||
|
if array_find(DECLARATIONS_TO_OMIT, decl.name) {
|
||||||
|
decl.decl_flags |= .OMIT_FROM_OUTPUT;
|
||||||
|
return .STOP;
|
||||||
|
}
|
||||||
|
if decl.name == "ImGuiContext" {
|
||||||
|
decl.output_name = decl.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if decl.kind == .TYPEDEF {
|
||||||
|
old_name := decl.name;
|
||||||
|
if old_name {
|
||||||
|
new_name := get_associated_enum_name(old_name);
|
||||||
|
for context.generator.global_scope.members {
|
||||||
|
if it.kind != .ENUM || it.name != new_name continue;
|
||||||
|
|
||||||
|
en := cast(*Enum)it;
|
||||||
|
decl.decl_flags |= .OMIT_FROM_OUTPUT;
|
||||||
|
|
||||||
|
if en.output_name.count > 2 && en.output_name[en.output_name.count - 1] == #char "_" {
|
||||||
|
en.output_name.count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if decl.comment.text && !en.comment.text {
|
||||||
|
en.comment = decl.comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
table_add(*enums_for_typedefs, old_name, en);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent_decl &&
|
||||||
|
(parent_decl.kind == .FUNCTION || parent_decl.kind == .STRUCT) &&
|
||||||
|
decl.kind == .DECLARATION && decl.type.type_of_typedef != null
|
||||||
|
{
|
||||||
|
old_name := decl.type.type_of_typedef.name;
|
||||||
|
if old_name {
|
||||||
|
found, en := table_find(*enums_for_typedefs, old_name);
|
||||||
|
if found {
|
||||||
|
change_type_to_enum(decl, en);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if decl.kind == .ENUM {
|
||||||
|
if ends_with(decl.name, "Flags_") {
|
||||||
|
en := cast(*Enum)decl;
|
||||||
|
en.flags |= .IS_ENUM_FLAGS;
|
||||||
|
en.flags |= .VALUES_IN_HEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if decl.kind == .FUNCTION {
|
||||||
|
func := cast(*Function)decl;
|
||||||
|
type := func.type.type_of_function;
|
||||||
|
for type.arguments {
|
||||||
|
if it_index == 0 || !ends_with(it.name, "_end") continue;
|
||||||
|
arg_type := find_underlying_type(it.type);
|
||||||
|
if !arg_type.pointer_to || !(arg_type.pointer_to.number_flags & ._8BIT) continue;
|
||||||
|
|
||||||
|
name_part := slice(it.name, 0, it.name.count - "_end".count);
|
||||||
|
if !name_part continue;
|
||||||
|
|
||||||
|
name_part_with_begin := tprint("%_begin", name_part);
|
||||||
|
prev_arg := type.arguments[it_index - 1];
|
||||||
|
if prev_arg.name == name_part || prev_arg.name == name_part_with_begin {
|
||||||
|
array_add(*begin_end_string_args, prev_arg);
|
||||||
|
func.decl_flags |= .NEEDS_ARGUMENT_WRAPPER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .RECURSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui_get_function_arg_for_printing :: (func: *Function, type: *Function_Type, mode: Arg_Print_Mode) -> []*Declaration {
|
||||||
|
args: [..]*Declaration;
|
||||||
|
for type.arguments {
|
||||||
|
if array_find(begin_end_string_args, it) {
|
||||||
|
if mode == {
|
||||||
|
case .OUTER_WRAPPER;
|
||||||
|
arg_copy := New(Declaration);
|
||||||
|
(arg_copy.*) = it.*;
|
||||||
|
arg_copy.type = context.generator.type_def_jai_string;
|
||||||
|
if ends_with(it.name, "_begin") {
|
||||||
|
arg_copy.output_name = slice(it.name, 0, it.name.count - "_begin".count);
|
||||||
|
}
|
||||||
|
array_add(*args, arg_copy);
|
||||||
|
it_index += 1;
|
||||||
|
continue;
|
||||||
|
case .INNER_WRAPPER_CALL;
|
||||||
|
base_arg_name := it.name;
|
||||||
|
if ends_with(it.name, "_begin") {
|
||||||
|
base_arg_name = slice(it.name, 0, it.name.count - "_begin".count);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_copy := New(Declaration);
|
||||||
|
(arg_copy.*) = it.*;
|
||||||
|
arg_copy.output_name = tprint("%.data", base_arg_name);
|
||||||
|
array_add(*args, arg_copy);
|
||||||
|
|
||||||
|
arg_copy_2 := New(Declaration);
|
||||||
|
(arg_copy_2.*) = it.*;
|
||||||
|
arg_copy_2.output_name = tprint("%.data + %.count", base_arg_name, base_arg_name);
|
||||||
|
array_add(*args, arg_copy_2);
|
||||||
|
|
||||||
|
it_index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_add(*args, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import "Basic";
|
||||||
|
#import "Bindings_Generator";
|
||||||
|
#import "BuildCpp";
|
||||||
|
#import "Compiler";
|
||||||
|
#import "File";
|
||||||
|
#import "Hash_Table";
|
||||||
|
#import "String";
|
||||||
30
modules/ImGui/module.jai
Normal file
30
modules/ImGui/module.jai
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// ImGui module for autosample — docking branch 1.92.7
|
||||||
|
// Core bindings are auto-generated in windows.jai via generate.jai.
|
||||||
|
// Backend and DockBuilder wrappers use C-linkage via imgui_backend_c.cpp.
|
||||||
|
|
||||||
|
ImVec2 :: Vector2;
|
||||||
|
ImVec4 :: Vector4;
|
||||||
|
ImColor :: Vector4;
|
||||||
|
|
||||||
|
ImVector :: struct (T: Type) {
|
||||||
|
Size: s32;
|
||||||
|
Capacity: s32;
|
||||||
|
Data: *T;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawCallback :: #type (parent_list: *ImDrawList, cmd: *ImDrawCmd) #c_call;
|
||||||
|
|
||||||
|
#scope_module
|
||||||
|
|
||||||
|
#import "Basic";
|
||||||
|
#import "Math";
|
||||||
|
|
||||||
|
#if OS == .WINDOWS {
|
||||||
|
imgui :: #library,no_dll "windows/ImGui";
|
||||||
|
#load "windows.jai";
|
||||||
|
#load "backend.jai";
|
||||||
|
#library,system,link_always "user32";
|
||||||
|
#library,system,link_always "gdi32";
|
||||||
|
#library,system,link_always "dwmapi";
|
||||||
|
#library,system,link_always "d3dcompiler";
|
||||||
|
}
|
||||||
77
modules/ImGui/src/imgui_backend_c.cpp
Normal file
77
modules/ImGui/src/imgui_backend_c.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Thin extern "C" wrapper for ImGui backend functions.
|
||||||
|
// This avoids C++ name mangling so Jai can call them with simple #foreign declarations.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
#include "imgui_impl_win32.h"
|
||||||
|
#include "imgui_impl_dx12.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// Forward declare — intentionally behind #if 0 in imgui_impl_win32.h
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
bool jai_ImGui_ImplWin32_Init(void* hwnd) {
|
||||||
|
return ImGui_ImplWin32_Init(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_ImGui_ImplWin32_Shutdown() {
|
||||||
|
ImGui_ImplWin32_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_ImGui_ImplWin32_NewFrame() {
|
||||||
|
ImGui_ImplWin32_NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT jai_ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
|
return ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jai_ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* info) {
|
||||||
|
return ImGui_ImplDX12_Init(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_ImGui_ImplDX12_Shutdown() {
|
||||||
|
ImGui_ImplDX12_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_ImGui_ImplDX12_NewFrame() {
|
||||||
|
ImGui_ImplDX12_NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* cmd_list) {
|
||||||
|
ImGui_ImplDX12_RenderDrawData(draw_data, cmd_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockBuilder functions (from imgui_internal.h)
|
||||||
|
void jai_DockBuilderRemoveNode(ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderRemoveNode(node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_DockBuilderAddNode(ImGuiID node_id, int flags) {
|
||||||
|
ImGui::DockBuilderAddNode(node_id, (ImGuiDockNodeFlags)flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) {
|
||||||
|
ImGui::DockBuilderSetNodeSize(node_id, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiID jai_DockBuilderSplitNode(ImGuiID node_id, int split_dir, float size_ratio, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir) {
|
||||||
|
return ImGui::DockBuilderSplitNode(node_id, (ImGuiDir)split_dir, size_ratio, out_id_at_dir, out_id_at_opposite_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_DockBuilderDockWindow(const char* window_name, ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderDockWindow(window_name, node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jai_DockBuilderFinish(ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderFinish(node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Font loading (convenience wrapper)
|
||||||
|
void* jai_AddFontFromFileTTF(const char* filename, float size_pixels) {
|
||||||
|
return ImGui::GetIO().Fonts->AddFontFromFileTTF(filename, size_pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
4490
modules/ImGui/windows.jai
Normal file
4490
modules/ImGui/windows.jai
Normal file
File diff suppressed because it is too large
Load Diff
72
nob.c
72
nob.c
@@ -1,72 +0,0 @@
|
|||||||
// Bootstrap: cl /nologo nob.c
|
|
||||||
// After that, just run: nob.exe
|
|
||||||
|
|
||||||
#define NOB_IMPLEMENTATION
|
|
||||||
#include "nob.h"
|
|
||||||
|
|
||||||
#define BUILD_DIR "build"
|
|
||||||
|
|
||||||
static const char *src_files[] = {
|
|
||||||
"src/main.cpp",
|
|
||||||
"src/platform/platform_win32.cpp",
|
|
||||||
"src/renderer/renderer_dx12.cpp",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *imgui_files[] = {
|
|
||||||
"vendor/imgui/imgui.cpp",
|
|
||||||
"vendor/imgui/imgui_draw.cpp",
|
|
||||||
"vendor/imgui/imgui_tables.cpp",
|
|
||||||
"vendor/imgui/imgui_widgets.cpp",
|
|
||||||
"vendor/imgui/imgui_demo.cpp",
|
|
||||||
"vendor/imgui/backends/imgui_impl_win32.cpp",
|
|
||||||
"vendor/imgui/backends/imgui_impl_dx12.cpp",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *link_libs[] = {
|
|
||||||
"d3d12.lib",
|
|
||||||
"dxgi.lib",
|
|
||||||
"d3dcompiler.lib",
|
|
||||||
"user32.lib",
|
|
||||||
"gdi32.lib",
|
|
||||||
"shell32.lib",
|
|
||||||
"ole32.lib",
|
|
||||||
"dwmapi.lib",
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
NOB_GO_REBUILD_URSELF(argc, argv);
|
|
||||||
|
|
||||||
if (!nob_mkdir_if_not_exists(BUILD_DIR)) return 1;
|
|
||||||
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
|
|
||||||
nob_cmd_append(&cmd, "cl.exe");
|
|
||||||
nob_cmd_append(&cmd, "/nologo", "/std:c++17", "/EHsc", "/W3");
|
|
||||||
nob_cmd_append(&cmd, "/Isrc", "/Ivendor/imgui", "/Ivendor/imgui/backends");
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
nob_cmd_append(&cmd, "/Zi", "/Od", "/D_DEBUG");
|
|
||||||
#else
|
|
||||||
nob_cmd_append(&cmd, "/O2", "/DNDEBUG");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
nob_cmd_append(&cmd, nob_temp_sprintf("/Fe:%s/autosample.exe", BUILD_DIR));
|
|
||||||
nob_cmd_append(&cmd, nob_temp_sprintf("/Fo:%s/", BUILD_DIR));
|
|
||||||
nob_cmd_append(&cmd, nob_temp_sprintf("/Fd:%s/", BUILD_DIR));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(src_files); i++)
|
|
||||||
nob_cmd_append(&cmd, src_files[i]);
|
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(imgui_files); i++)
|
|
||||||
nob_cmd_append(&cmd, imgui_files[i]);
|
|
||||||
|
|
||||||
nob_cmd_append(&cmd, "/link");
|
|
||||||
nob_cmd_append(&cmd, "/SUBSYSTEM:CONSOLE");
|
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(link_libs); i++)
|
|
||||||
nob_cmd_append(&cmd, link_libs[i]);
|
|
||||||
|
|
||||||
if (!nob_cmd_run(&cmd)) return 1;
|
|
||||||
|
|
||||||
nob_log(NOB_INFO, "Build complete: %s/autosample.exe", BUILD_DIR);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
6
src/.build/.added_strings_w2.jai
Normal file
6
src/.build/.added_strings_w2.jai
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Workspace: Target Program
|
||||||
|
|
||||||
|
//
|
||||||
|
// String added via add_build_string() from c:/Users/mta/.vscode-oss/extensions/apparentlystudio.jails-0.2.0-universal/out/metaprogram/jails_diagnostics.jai:19.
|
||||||
|
//
|
||||||
|
JAILS_DIAGNOSTICS_BUILD :: true;
|
||||||
278
src/main.cpp
278
src/main.cpp
@@ -1,278 +0,0 @@
|
|||||||
#include "platform/platform.h"
|
|
||||||
#include "renderer/renderer.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_internal.h"
|
|
||||||
|
|
||||||
enum MenuCmd {
|
|
||||||
MENU_NONE = 0,
|
|
||||||
MENU_FILE_NEW,
|
|
||||||
MENU_FILE_OPEN,
|
|
||||||
MENU_FILE_SAVE,
|
|
||||||
MENU_FILE_SAVE_AS,
|
|
||||||
MENU_FILE_EXIT,
|
|
||||||
MENU_IMPORT_AUDIO,
|
|
||||||
MENU_IMPORT_MIDI,
|
|
||||||
MENU_VIEW_BROWSER,
|
|
||||||
MENU_VIEW_PROPERTIES,
|
|
||||||
MENU_VIEW_LOG,
|
|
||||||
MENU_VIEW_DEMO,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void setup_menus(PlatformWindow *window)
|
|
||||||
{
|
|
||||||
PlatformMenuItem file_items[] = {
|
|
||||||
{ "New", MENU_FILE_NEW },
|
|
||||||
{ "Open...", MENU_FILE_OPEN },
|
|
||||||
{ "Save", MENU_FILE_SAVE },
|
|
||||||
{ "Save As...", MENU_FILE_SAVE_AS },
|
|
||||||
{ nullptr, 0 },
|
|
||||||
{ "Exit", MENU_FILE_EXIT },
|
|
||||||
};
|
|
||||||
|
|
||||||
PlatformMenuItem import_items[] = {
|
|
||||||
{ "Audio...", MENU_IMPORT_AUDIO },
|
|
||||||
{ "MIDI...", MENU_IMPORT_MIDI },
|
|
||||||
};
|
|
||||||
|
|
||||||
PlatformMenuItem view_items[] = {
|
|
||||||
{ "Browser", MENU_VIEW_BROWSER },
|
|
||||||
{ "Properties", MENU_VIEW_PROPERTIES },
|
|
||||||
{ "Log", MENU_VIEW_LOG },
|
|
||||||
{ nullptr, 0 },
|
|
||||||
{ "Demo", MENU_VIEW_DEMO },
|
|
||||||
};
|
|
||||||
|
|
||||||
PlatformMenu menus[] = {
|
|
||||||
{ "File", file_items, sizeof(file_items) / sizeof(file_items[0]) },
|
|
||||||
{ "Import", import_items, sizeof(import_items) / sizeof(import_items[0]) },
|
|
||||||
{ "View", view_items, sizeof(view_items) / sizeof(view_items[0]) },
|
|
||||||
};
|
|
||||||
|
|
||||||
platform_set_menu(window, menus, sizeof(menus) / sizeof(menus[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_theme()
|
|
||||||
{
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
|
||||||
|
|
||||||
// Load Segoe UI from Windows system fonts
|
|
||||||
ImFontConfig font_cfg = {};
|
|
||||||
font_cfg.OversampleH = 2;
|
|
||||||
font_cfg.OversampleV = 1;
|
|
||||||
font_cfg.PixelSnapH = true;
|
|
||||||
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 15.0f, &font_cfg);
|
|
||||||
|
|
||||||
// DAW-style dark theme
|
|
||||||
ImGuiStyle &s = ImGui::GetStyle();
|
|
||||||
|
|
||||||
// Geometry
|
|
||||||
s.WindowPadding = ImVec2(8, 8);
|
|
||||||
s.FramePadding = ImVec2(6, 4);
|
|
||||||
s.ItemSpacing = ImVec2(8, 4);
|
|
||||||
s.ItemInnerSpacing = ImVec2(4, 4);
|
|
||||||
s.ScrollbarSize = 12.0f;
|
|
||||||
s.GrabMinSize = 8.0f;
|
|
||||||
s.WindowBorderSize = 1.0f;
|
|
||||||
s.FrameBorderSize = 0.0f;
|
|
||||||
s.TabBorderSize = 0.0f;
|
|
||||||
s.WindowRounding = 2.0f;
|
|
||||||
s.FrameRounding = 2.0f;
|
|
||||||
s.GrabRounding = 2.0f;
|
|
||||||
s.TabRounding = 2.0f;
|
|
||||||
s.ScrollbarRounding = 2.0f;
|
|
||||||
|
|
||||||
ImVec4 *c = s.Colors;
|
|
||||||
|
|
||||||
// Backgrounds
|
|
||||||
c[ImGuiCol_WindowBg] = ImVec4(0.12f, 0.12f, 0.13f, 1.00f);
|
|
||||||
c[ImGuiCol_ChildBg] = ImVec4(0.12f, 0.12f, 0.13f, 1.00f);
|
|
||||||
c[ImGuiCol_PopupBg] = ImVec4(0.15f, 0.15f, 0.16f, 1.00f);
|
|
||||||
|
|
||||||
// Borders
|
|
||||||
c[ImGuiCol_Border] = ImVec4(0.22f, 0.22f, 0.24f, 1.00f);
|
|
||||||
c[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
|
||||||
|
|
||||||
// Text
|
|
||||||
c[ImGuiCol_Text] = ImVec4(0.88f, 0.88f, 0.88f, 1.00f);
|
|
||||||
c[ImGuiCol_TextDisabled] = ImVec4(0.44f, 0.44f, 0.44f, 1.00f);
|
|
||||||
|
|
||||||
// Headers (collapsing headers, menu bar items)
|
|
||||||
c[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.22f, 1.00f);
|
|
||||||
c[ImGuiCol_HeaderHovered] = ImVec4(0.28f, 0.28f, 0.30f, 1.00f);
|
|
||||||
c[ImGuiCol_HeaderActive] = ImVec4(0.24f, 0.24f, 0.26f, 1.00f);
|
|
||||||
|
|
||||||
// Buttons
|
|
||||||
c[ImGuiCol_Button] = ImVec4(0.22f, 0.22f, 0.24f, 1.00f);
|
|
||||||
c[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.30f, 0.33f, 1.00f);
|
|
||||||
c[ImGuiCol_ButtonActive] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f);
|
|
||||||
|
|
||||||
// Frame backgrounds (inputs, checkboxes, sliders)
|
|
||||||
c[ImGuiCol_FrameBg] = ImVec4(0.16f, 0.16f, 0.17f, 1.00f);
|
|
||||||
c[ImGuiCol_FrameBgHovered] = ImVec4(0.20f, 0.20f, 0.22f, 1.00f);
|
|
||||||
c[ImGuiCol_FrameBgActive] = ImVec4(0.18f, 0.18f, 0.20f, 1.00f);
|
|
||||||
|
|
||||||
// Tabs
|
|
||||||
c[ImGuiCol_Tab] = ImVec4(0.16f, 0.16f, 0.17f, 1.00f);
|
|
||||||
c[ImGuiCol_TabHovered] = ImVec4(0.28f, 0.28f, 0.30f, 1.00f);
|
|
||||||
c[ImGuiCol_TabSelected] = ImVec4(0.20f, 0.20f, 0.22f, 1.00f);
|
|
||||||
c[ImGuiCol_TabSelectedOverline] = ImVec4(0.34f, 0.54f, 0.69f, 1.00f);
|
|
||||||
c[ImGuiCol_TabDimmed] = ImVec4(0.12f, 0.12f, 0.13f, 1.00f);
|
|
||||||
c[ImGuiCol_TabDimmedSelected] = ImVec4(0.16f, 0.16f, 0.17f, 1.00f);
|
|
||||||
|
|
||||||
// Title bar
|
|
||||||
c[ImGuiCol_TitleBg] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f);
|
|
||||||
c[ImGuiCol_TitleBgActive] = ImVec4(0.13f, 0.13f, 0.14f, 1.00f);
|
|
||||||
c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f);
|
|
||||||
|
|
||||||
// Scrollbar
|
|
||||||
c[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f);
|
|
||||||
c[ImGuiCol_ScrollbarGrab] = ImVec4(0.24f, 0.24f, 0.26f, 1.00f);
|
|
||||||
c[ImGuiCol_ScrollbarGrabHovered]= ImVec4(0.30f, 0.30f, 0.33f, 1.00f);
|
|
||||||
c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.34f, 0.34f, 0.37f, 1.00f);
|
|
||||||
|
|
||||||
// Slider grab
|
|
||||||
c[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.54f, 0.69f, 1.00f);
|
|
||||||
c[ImGuiCol_SliderGrabActive] = ImVec4(0.40f, 0.60f, 0.75f, 1.00f);
|
|
||||||
|
|
||||||
// Checkmark, accent
|
|
||||||
c[ImGuiCol_CheckMark] = ImVec4(0.34f, 0.54f, 0.69f, 1.00f);
|
|
||||||
|
|
||||||
// Separator
|
|
||||||
c[ImGuiCol_Separator] = ImVec4(0.22f, 0.22f, 0.24f, 1.00f);
|
|
||||||
c[ImGuiCol_SeparatorHovered] = ImVec4(0.34f, 0.54f, 0.69f, 1.00f);
|
|
||||||
c[ImGuiCol_SeparatorActive] = ImVec4(0.34f, 0.54f, 0.69f, 1.00f);
|
|
||||||
|
|
||||||
// Resize grip
|
|
||||||
c[ImGuiCol_ResizeGrip] = ImVec4(0.22f, 0.22f, 0.24f, 0.50f);
|
|
||||||
c[ImGuiCol_ResizeGripHovered] = ImVec4(0.34f, 0.54f, 0.69f, 0.67f);
|
|
||||||
c[ImGuiCol_ResizeGripActive] = ImVec4(0.34f, 0.54f, 0.69f, 0.95f);
|
|
||||||
|
|
||||||
// Docking
|
|
||||||
c[ImGuiCol_DockingPreview] = ImVec4(0.34f, 0.54f, 0.69f, 0.70f);
|
|
||||||
c[ImGuiCol_DockingEmptyBg] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f);
|
|
||||||
|
|
||||||
// Menu bar
|
|
||||||
c[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.15f, 1.00f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void build_default_layout(ImGuiID dockspace_id)
|
|
||||||
{
|
|
||||||
ImGui::DockBuilderRemoveNode(dockspace_id);
|
|
||||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
|
||||||
ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size);
|
|
||||||
|
|
||||||
ImGuiID center = dockspace_id;
|
|
||||||
ImGuiID left = ImGui::DockBuilderSplitNode(center, ImGuiDir_Left, 0.15f, nullptr, ¢er);
|
|
||||||
ImGuiID right = ImGui::DockBuilderSplitNode(center, ImGuiDir_Right, 0.20f, nullptr, ¢er);
|
|
||||||
ImGuiID bottom = ImGui::DockBuilderSplitNode(center, ImGuiDir_Down, 0.25f, nullptr, ¢er);
|
|
||||||
|
|
||||||
ImGui::DockBuilderDockWindow("Browser", left);
|
|
||||||
ImGui::DockBuilderDockWindow("Main", center);
|
|
||||||
ImGui::DockBuilderDockWindow("Properties", right);
|
|
||||||
ImGui::DockBuilderDockWindow("Log", bottom);
|
|
||||||
|
|
||||||
ImGui::DockBuilderFinish(dockspace_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
|
|
||||||
PlatformWindowDesc window_desc = {};
|
|
||||||
PlatformWindow *window = platform_create_window(&window_desc);
|
|
||||||
if (!window)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
int32_t w, h;
|
|
||||||
platform_get_size(window, &w, &h);
|
|
||||||
|
|
||||||
RendererDesc renderer_desc = {};
|
|
||||||
renderer_desc.window_handle = platform_get_native_handle(window);
|
|
||||||
renderer_desc.width = w;
|
|
||||||
renderer_desc.height = h;
|
|
||||||
Renderer *renderer = renderer_create(&renderer_desc);
|
|
||||||
if (!renderer) {
|
|
||||||
platform_destroy_window(window);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_theme();
|
|
||||||
setup_menus(window);
|
|
||||||
|
|
||||||
int32_t last_w = w, last_h = h;
|
|
||||||
bool show_demo = true;
|
|
||||||
bool show_browser = true;
|
|
||||||
bool show_props = true;
|
|
||||||
bool show_log = true;
|
|
||||||
bool first_frame = true;
|
|
||||||
|
|
||||||
while (platform_poll_events(window)) {
|
|
||||||
int32_t menu_cmd = platform_poll_menu_command(window);
|
|
||||||
switch (menu_cmd) {
|
|
||||||
case MENU_FILE_EXIT: platform_destroy_window(window); return 0;
|
|
||||||
case MENU_VIEW_BROWSER: show_browser = !show_browser; break;
|
|
||||||
case MENU_VIEW_PROPERTIES:show_props = !show_props; break;
|
|
||||||
case MENU_VIEW_LOG: show_log = !show_log; break;
|
|
||||||
case MENU_VIEW_DEMO: show_demo = !show_demo; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
platform_get_size(window, &w, &h);
|
|
||||||
if (w != last_w || h != last_h) {
|
|
||||||
renderer_resize(renderer, w, h);
|
|
||||||
last_w = w;
|
|
||||||
last_h = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderer_begin_frame(renderer))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Full-window dockspace
|
|
||||||
ImGuiID dockspace_id = ImGui::GetID("MainDockSpace");
|
|
||||||
ImGui::DockSpaceOverViewport(dockspace_id, ImGui::GetMainViewport());
|
|
||||||
|
|
||||||
if (first_frame) {
|
|
||||||
build_default_layout(dockspace_id);
|
|
||||||
first_frame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left panel
|
|
||||||
if (show_browser) {
|
|
||||||
ImGui::Begin("Browser", &show_browser);
|
|
||||||
ImGui::Text("Instruments");
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main content
|
|
||||||
ImGui::Begin("Main");
|
|
||||||
ImGui::Text("Main content area");
|
|
||||||
ImGui::End();
|
|
||||||
|
|
||||||
// Right panel
|
|
||||||
if (show_props) {
|
|
||||||
ImGui::Begin("Properties", &show_props);
|
|
||||||
ImGui::Text("Details");
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom panel
|
|
||||||
if (show_log) {
|
|
||||||
ImGui::Begin("Log", &show_log);
|
|
||||||
ImGui::Text("Output / Log");
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Demo window
|
|
||||||
if (show_demo)
|
|
||||||
ImGui::ShowDemoWindow(&show_demo);
|
|
||||||
|
|
||||||
renderer_end_frame(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer_destroy(renderer);
|
|
||||||
platform_destroy_window(window);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
264
src/main.jai
Normal file
264
src/main.jai
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
// autosample — Jai + DX12 + ImGui (docking) DAW-style application
|
||||||
|
|
||||||
|
#import "Basic";
|
||||||
|
#import "Windows";
|
||||||
|
#import "Windows_Utf8";
|
||||||
|
#import "d3d12"()(INCLUDE_DEBUG_BINDINGS = true);
|
||||||
|
dxgi :: #import "dxgi";
|
||||||
|
|
||||||
|
ImGui :: #import "ImGui";
|
||||||
|
|
||||||
|
Window :: #import "Window_Creation";
|
||||||
|
Input :: #import "Input";
|
||||||
|
|
||||||
|
#load "platform/platform.jai";
|
||||||
|
#load "renderer/renderer.jai";
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Menu command IDs
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
MENU_FILE_NEW :: 1;
|
||||||
|
MENU_FILE_OPEN :: 2;
|
||||||
|
MENU_FILE_SAVE :: 3;
|
||||||
|
MENU_FILE_SAVE_AS :: 4;
|
||||||
|
MENU_FILE_EXIT :: 5;
|
||||||
|
MENU_IMPORT_AUDIO :: 6;
|
||||||
|
MENU_IMPORT_MIDI :: 7;
|
||||||
|
MENU_VIEW_BROWSER :: 8;
|
||||||
|
MENU_VIEW_PROPERTIES :: 9;
|
||||||
|
MENU_VIEW_LOG :: 10;
|
||||||
|
MENU_VIEW_DEMO :: 11;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// DAW Theme
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
setup_theme :: () {
|
||||||
|
io := ImGui.GetIO();
|
||||||
|
ImGui.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 15.0);
|
||||||
|
|
||||||
|
s := ImGui.GetStyle();
|
||||||
|
s.WindowPadding = .{8, 8};
|
||||||
|
s.FramePadding = .{6, 4};
|
||||||
|
s.ItemSpacing = .{8, 4};
|
||||||
|
s.ItemInnerSpacing = .{4, 4};
|
||||||
|
s.ScrollbarSize = 12.0;
|
||||||
|
s.GrabMinSize = 8.0;
|
||||||
|
s.WindowBorderSize = 1.0;
|
||||||
|
s.FrameBorderSize = 0.0;
|
||||||
|
s.TabBorderSize = 0.0;
|
||||||
|
s.WindowRounding = 2.0;
|
||||||
|
s.FrameRounding = 2.0;
|
||||||
|
s.GrabRounding = 2.0;
|
||||||
|
s.TabRounding = 2.0;
|
||||||
|
s.ScrollbarRounding = 2.0;
|
||||||
|
|
||||||
|
// Backgrounds
|
||||||
|
s.Colors[ImGui.Col.WindowBg] = .{0.12, 0.12, 0.13, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ChildBg] = .{0.12, 0.12, 0.13, 1.00};
|
||||||
|
s.Colors[ImGui.Col.PopupBg] = .{0.15, 0.15, 0.16, 1.00};
|
||||||
|
|
||||||
|
// Borders
|
||||||
|
s.Colors[ImGui.Col.Border] = .{0.22, 0.22, 0.24, 1.00};
|
||||||
|
s.Colors[ImGui.Col.BorderShadow] = .{0.00, 0.00, 0.00, 0.00};
|
||||||
|
|
||||||
|
// Text
|
||||||
|
s.Colors[ImGui.Col.Text] = .{0.88, 0.88, 0.88, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TextDisabled] = .{0.44, 0.44, 0.44, 1.00};
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
s.Colors[ImGui.Col.Header] = .{0.20, 0.20, 0.22, 1.00};
|
||||||
|
s.Colors[ImGui.Col.HeaderHovered] = .{0.28, 0.28, 0.30, 1.00};
|
||||||
|
s.Colors[ImGui.Col.HeaderActive] = .{0.24, 0.24, 0.26, 1.00};
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
s.Colors[ImGui.Col.Button] = .{0.22, 0.22, 0.24, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ButtonHovered] = .{0.30, 0.30, 0.33, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ButtonActive] = .{0.26, 0.26, 0.28, 1.00};
|
||||||
|
|
||||||
|
// Frame backgrounds
|
||||||
|
s.Colors[ImGui.Col.FrameBg] = .{0.16, 0.16, 0.17, 1.00};
|
||||||
|
s.Colors[ImGui.Col.FrameBgHovered] = .{0.20, 0.20, 0.22, 1.00};
|
||||||
|
s.Colors[ImGui.Col.FrameBgActive] = .{0.18, 0.18, 0.20, 1.00};
|
||||||
|
|
||||||
|
// Tabs
|
||||||
|
s.Colors[ImGui.Col.Tab] = .{0.16, 0.16, 0.17, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TabHovered] = .{0.28, 0.28, 0.30, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TabSelected] = .{0.20, 0.20, 0.22, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TabSelectedOverline] = .{0.34, 0.54, 0.69, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TabDimmed] = .{0.12, 0.12, 0.13, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TabDimmedSelected] = .{0.16, 0.16, 0.17, 1.00};
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
s.Colors[ImGui.Col.TitleBg] = .{0.10, 0.10, 0.11, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TitleBgActive] = .{0.13, 0.13, 0.14, 1.00};
|
||||||
|
s.Colors[ImGui.Col.TitleBgCollapsed] = .{0.10, 0.10, 0.11, 1.00};
|
||||||
|
|
||||||
|
// Scrollbar
|
||||||
|
s.Colors[ImGui.Col.ScrollbarBg] = .{0.10, 0.10, 0.11, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ScrollbarGrab] = .{0.24, 0.24, 0.26, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ScrollbarGrabHovered]= .{0.30, 0.30, 0.33, 1.00};
|
||||||
|
s.Colors[ImGui.Col.ScrollbarGrabActive] = .{0.34, 0.34, 0.37, 1.00};
|
||||||
|
|
||||||
|
// Slider grab
|
||||||
|
s.Colors[ImGui.Col.SliderGrab] = .{0.34, 0.54, 0.69, 1.00};
|
||||||
|
s.Colors[ImGui.Col.SliderGrabActive] = .{0.40, 0.60, 0.75, 1.00};
|
||||||
|
|
||||||
|
// Checkmark
|
||||||
|
s.Colors[ImGui.Col.CheckMark] = .{0.34, 0.54, 0.69, 1.00};
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
s.Colors[ImGui.Col.Separator] = .{0.22, 0.22, 0.24, 1.00};
|
||||||
|
s.Colors[ImGui.Col.SeparatorHovered] = .{0.34, 0.54, 0.69, 1.00};
|
||||||
|
s.Colors[ImGui.Col.SeparatorActive] = .{0.34, 0.54, 0.69, 1.00};
|
||||||
|
|
||||||
|
// Resize grip
|
||||||
|
s.Colors[ImGui.Col.ResizeGrip] = .{0.22, 0.22, 0.24, 0.50};
|
||||||
|
s.Colors[ImGui.Col.ResizeGripHovered] = .{0.34, 0.54, 0.69, 0.67};
|
||||||
|
s.Colors[ImGui.Col.ResizeGripActive] = .{0.34, 0.54, 0.69, 0.95};
|
||||||
|
|
||||||
|
// Docking
|
||||||
|
s.Colors[ImGui.Col.DockingPreview] = .{0.34, 0.54, 0.69, 0.70};
|
||||||
|
s.Colors[ImGui.Col.DockingEmptyBg] = .{0.10, 0.10, 0.11, 1.00};
|
||||||
|
|
||||||
|
// Menu bar
|
||||||
|
s.Colors[ImGui.Col.MenuBarBg] = .{0.14, 0.14, 0.15, 1.00};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Docking Layout
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
build_default_layout :: (dockspace_id: ImGui.ID) {
|
||||||
|
viewport := ImGui.GetMainViewport();
|
||||||
|
|
||||||
|
ImGui.DockBuilderRemoveNode(dockspace_id);
|
||||||
|
ImGui.DockBuilderAddNode(dockspace_id, xx ImGui.DockNodeFlags.None | xx 1 << 5); // ImGuiDockNodeFlags_DockSpace = 1 << 5 (internal flag)
|
||||||
|
ImGui.DockBuilderSetNodeSize(dockspace_id, viewport.Size);
|
||||||
|
|
||||||
|
center := dockspace_id;
|
||||||
|
left: ImGui.ID;
|
||||||
|
right: ImGui.ID;
|
||||||
|
bottom: ImGui.ID;
|
||||||
|
|
||||||
|
ImGui.DockBuilderSplitNode(center, xx ImGui.Dir.Left, 0.15, *left, *center);
|
||||||
|
ImGui.DockBuilderSplitNode(center, xx ImGui.Dir.Right, 0.20, *right, *center);
|
||||||
|
ImGui.DockBuilderSplitNode(center, xx ImGui.Dir.Down, 0.25, *bottom, *center);
|
||||||
|
|
||||||
|
ImGui.DockBuilderDockWindow("Browser", left);
|
||||||
|
ImGui.DockBuilderDockWindow("Main", center);
|
||||||
|
ImGui.DockBuilderDockWindow("Properties", right);
|
||||||
|
ImGui.DockBuilderDockWindow("Log", bottom);
|
||||||
|
|
||||||
|
ImGui.DockBuilderFinish(dockspace_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Main
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
main :: () {
|
||||||
|
if !platform_init() return;
|
||||||
|
|
||||||
|
// Init DX12
|
||||||
|
if !create_device() { print("create_device failed\n"); return; }
|
||||||
|
if !create_command_queue() { print("create_command_queue failed\n"); return; }
|
||||||
|
if !create_descriptor_heaps() { print("create_descriptor_heaps failed\n"); return; }
|
||||||
|
if !create_frame_resources() { print("create_frame_resources failed\n"); return; }
|
||||||
|
if !create_swap_chain() { print("create_swap_chain failed\n"); return; }
|
||||||
|
create_render_targets();
|
||||||
|
|
||||||
|
// Init ImGui
|
||||||
|
init_imgui();
|
||||||
|
setup_theme();
|
||||||
|
setup_menus();
|
||||||
|
|
||||||
|
// Application state
|
||||||
|
show_demo := true;
|
||||||
|
show_browser := true;
|
||||||
|
show_props := true;
|
||||||
|
show_log := true;
|
||||||
|
first_frame := true;
|
||||||
|
last_w := w_width;
|
||||||
|
last_h := w_height;
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
quit := false;
|
||||||
|
while !quit {
|
||||||
|
msg: MSG;
|
||||||
|
while PeekMessageW(*msg, null, 0, 0, PM_REMOVE) != 0 {
|
||||||
|
TranslateMessage(*msg);
|
||||||
|
DispatchMessageW(*msg);
|
||||||
|
if msg.message == WM_QUIT then quit = true;
|
||||||
|
}
|
||||||
|
if quit break;
|
||||||
|
|
||||||
|
// Handle menu commands
|
||||||
|
if pending_menu_cmd != 0 {
|
||||||
|
cmd := pending_menu_cmd;
|
||||||
|
pending_menu_cmd = 0;
|
||||||
|
if cmd == {
|
||||||
|
case MENU_FILE_EXIT; quit = true;
|
||||||
|
case MENU_VIEW_BROWSER; show_browser = !show_browser;
|
||||||
|
case MENU_VIEW_PROPERTIES; show_props = !show_props;
|
||||||
|
case MENU_VIEW_LOG; show_log = !show_log;
|
||||||
|
case MENU_VIEW_DEMO; show_demo = !show_demo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if quit break;
|
||||||
|
|
||||||
|
// Handle resize
|
||||||
|
if w_width != last_w || w_height != last_h {
|
||||||
|
resize(w_width, w_height);
|
||||||
|
last_w = w_width;
|
||||||
|
last_h = w_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !begin_frame() continue;
|
||||||
|
|
||||||
|
// Full-window dockspace
|
||||||
|
dockspace_id := ImGui.GetID("MainDockSpace");
|
||||||
|
ImGui.DockSpaceOverViewport(dockspace_id, ImGui.GetMainViewport());
|
||||||
|
|
||||||
|
if first_frame {
|
||||||
|
build_default_layout(dockspace_id);
|
||||||
|
first_frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left panel
|
||||||
|
if show_browser {
|
||||||
|
ImGui.Begin("Browser", *show_browser);
|
||||||
|
ImGui.Text("Instruments");
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main content
|
||||||
|
ImGui.Begin("Main");
|
||||||
|
ImGui.Text("Main content area");
|
||||||
|
ImGui.End();
|
||||||
|
|
||||||
|
// Right panel
|
||||||
|
if show_props {
|
||||||
|
ImGui.Begin("Properties", *show_props);
|
||||||
|
ImGui.Text("Details");
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom panel
|
||||||
|
if show_log {
|
||||||
|
ImGui.Begin("Log", *show_log);
|
||||||
|
ImGui.Text("Output / Log");
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demo window
|
||||||
|
if show_demo {
|
||||||
|
ImGui.ShowDemoWindow(*show_demo);
|
||||||
|
}
|
||||||
|
|
||||||
|
end_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer_cleanup();
|
||||||
|
platform_shutdown();
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
struct PlatformWindow;
|
|
||||||
|
|
||||||
struct PlatformWindowDesc {
|
|
||||||
const char *title = "autosample";
|
|
||||||
int32_t width = 1280;
|
|
||||||
int32_t height = 720;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlatformMenuItem {
|
|
||||||
const char *label; // nullptr = separator
|
|
||||||
int32_t id; // command ID (ignored for separators)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlatformMenu {
|
|
||||||
const char *label;
|
|
||||||
PlatformMenuItem *items;
|
|
||||||
int32_t item_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc);
|
|
||||||
void platform_destroy_window(PlatformWindow *window);
|
|
||||||
bool platform_poll_events(PlatformWindow *window);
|
|
||||||
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h);
|
|
||||||
void *platform_get_native_handle(PlatformWindow *window);
|
|
||||||
|
|
||||||
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count);
|
|
||||||
int32_t platform_poll_menu_command(PlatformWindow *window);
|
|
||||||
139
src/platform/platform.jai
Normal file
139
src/platform/platform.jai
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
// autosample — Win32 platform layer
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Missing Win32 declarations
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
CS_CLASSDC :: 0x0040;
|
||||||
|
|
||||||
|
user32 :: #library,system "user32";
|
||||||
|
|
||||||
|
CreateMenu :: () -> HMENU #foreign user32;
|
||||||
|
CreatePopupMenu :: () -> HMENU #foreign user32;
|
||||||
|
AppendMenuW :: (hMenu: HMENU, uFlags: u32, uIDNewItem: u64, lpNewItem: *u16) -> s32 #foreign user32;
|
||||||
|
SetMenu :: (hWnd: HWND, hMenu: HMENU) -> s32 #foreign user32;
|
||||||
|
|
||||||
|
MF_STRING :: 0x0000;
|
||||||
|
MF_SEPARATOR :: 0x0800;
|
||||||
|
MF_POPUP :: 0x0010;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Platform state
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
hwnd: HWND;
|
||||||
|
w_width: u32 = 1280;
|
||||||
|
w_height: u32 = 800;
|
||||||
|
|
||||||
|
pending_menu_cmd: s32;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// WndProc
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
my_wndproc :: (window_hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #c_call {
|
||||||
|
// Forward to ImGui
|
||||||
|
result := ImGui.ImplWin32_WndProcHandler(window_hwnd, msg, wparam, lparam);
|
||||||
|
if result return result;
|
||||||
|
|
||||||
|
if msg == {
|
||||||
|
case WM_SIZE;
|
||||||
|
if wparam != SIZE_MINIMIZED {
|
||||||
|
w_width = cast(u32) (lparam & 0xFFFF);
|
||||||
|
w_height = cast(u32) ((lparam >> 16) & 0xFFFF);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case WM_COMMAND;
|
||||||
|
if (wparam >> 16) == 0 // HIWORD == 0 means menu
|
||||||
|
pending_menu_cmd = cast(s32) (wparam & 0xFFFF);
|
||||||
|
return 0;
|
||||||
|
case WM_SYSCOMMAND;
|
||||||
|
if (wparam & 0xfff0) == SC_KEYMENU return 0;
|
||||||
|
case WM_DESTROY;
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProcW(window_hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Native Win32 Menus
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
menu_bar_handle: HMENU;
|
||||||
|
|
||||||
|
setup_menus :: () {
|
||||||
|
menu_bar_handle = CreateMenu();
|
||||||
|
|
||||||
|
// File menu
|
||||||
|
file_menu := CreatePopupMenu();
|
||||||
|
AppendMenuW(file_menu, MF_STRING, xx MENU_FILE_NEW, utf8_to_wide("New"));
|
||||||
|
AppendMenuW(file_menu, MF_STRING, xx MENU_FILE_OPEN, utf8_to_wide("Open..."));
|
||||||
|
AppendMenuW(file_menu, MF_STRING, xx MENU_FILE_SAVE, utf8_to_wide("Save"));
|
||||||
|
AppendMenuW(file_menu, MF_STRING, xx MENU_FILE_SAVE_AS, utf8_to_wide("Save As..."));
|
||||||
|
AppendMenuW(file_menu, MF_SEPARATOR, 0, null);
|
||||||
|
AppendMenuW(file_menu, MF_STRING, xx MENU_FILE_EXIT, utf8_to_wide("Exit"));
|
||||||
|
AppendMenuW(menu_bar_handle, MF_POPUP, xx file_menu, utf8_to_wide("File"));
|
||||||
|
|
||||||
|
// Import menu
|
||||||
|
import_menu := CreatePopupMenu();
|
||||||
|
AppendMenuW(import_menu, MF_STRING, xx MENU_IMPORT_AUDIO, utf8_to_wide("Audio..."));
|
||||||
|
AppendMenuW(import_menu, MF_STRING, xx MENU_IMPORT_MIDI, utf8_to_wide("MIDI..."));
|
||||||
|
AppendMenuW(menu_bar_handle, MF_POPUP, xx import_menu, utf8_to_wide("Import"));
|
||||||
|
|
||||||
|
// View menu
|
||||||
|
view_menu := CreatePopupMenu();
|
||||||
|
AppendMenuW(view_menu, MF_STRING, xx MENU_VIEW_BROWSER, utf8_to_wide("Browser"));
|
||||||
|
AppendMenuW(view_menu, MF_STRING, xx MENU_VIEW_PROPERTIES, utf8_to_wide("Properties"));
|
||||||
|
AppendMenuW(view_menu, MF_STRING, xx MENU_VIEW_LOG, utf8_to_wide("Log"));
|
||||||
|
AppendMenuW(view_menu, MF_SEPARATOR, 0, null);
|
||||||
|
AppendMenuW(view_menu, MF_STRING, xx MENU_VIEW_DEMO, utf8_to_wide("Demo"));
|
||||||
|
AppendMenuW(menu_bar_handle, MF_POPUP, xx view_menu, utf8_to_wide("View"));
|
||||||
|
|
||||||
|
SetMenu(hwnd, menu_bar_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Platform Init / Shutdown
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
platform_init :: () -> bool {
|
||||||
|
wc: WNDCLASSEXW;
|
||||||
|
wc.cbSize = size_of(WNDCLASSEXW);
|
||||||
|
wc.style = CS_CLASSDC;
|
||||||
|
wc.lpfnWndProc = xx my_wndproc;
|
||||||
|
wc.hInstance = GetModuleHandleW(null);
|
||||||
|
wc.lpszClassName = utf8_to_wide("autosample_wc");
|
||||||
|
RegisterClassExW(*wc);
|
||||||
|
|
||||||
|
screen_w := GetSystemMetrics(SM_CXSCREEN);
|
||||||
|
screen_h := GetSystemMetrics(SM_CYSCREEN);
|
||||||
|
x := (screen_w - cast(s32) w_width) / 2;
|
||||||
|
y := (screen_h - cast(s32) w_height) / 2;
|
||||||
|
|
||||||
|
rect: RECT;
|
||||||
|
rect.right = cast(s32) w_width;
|
||||||
|
rect.bottom = cast(s32) w_height;
|
||||||
|
AdjustWindowRect(*rect, WS_OVERLAPPEDWINDOW, 0);
|
||||||
|
|
||||||
|
hwnd = CreateWindowExW(
|
||||||
|
0,
|
||||||
|
wc.lpszClassName,
|
||||||
|
utf8_to_wide("autosample"),
|
||||||
|
WS_OVERLAPPEDWINDOW,
|
||||||
|
x, y,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
|
null, null, wc.hInstance, null
|
||||||
|
);
|
||||||
|
|
||||||
|
if !hwnd {
|
||||||
|
print("CreateWindowExW failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowWindow(hwnd, SW_SHOWDEFAULT);
|
||||||
|
UpdateWindow(hwnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_shutdown :: () {
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
UnregisterClassW(utf8_to_wide("autosample_wc"), GetModuleHandleW(null));
|
||||||
|
}
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
#include "platform/platform.h"
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
#include "imgui_impl_win32.h"
|
|
||||||
|
|
||||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
||||||
|
|
||||||
struct PlatformWindow {
|
|
||||||
HWND hwnd;
|
|
||||||
bool should_close;
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
int32_t pending_menu_cmd;
|
|
||||||
};
|
|
||||||
|
|
||||||
static PlatformWindow *g_current_window = nullptr;
|
|
||||||
|
|
||||||
static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|
||||||
{
|
|
||||||
if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
switch (msg) {
|
|
||||||
case WM_SIZE:
|
|
||||||
if (g_current_window && wparam != SIZE_MINIMIZED) {
|
|
||||||
g_current_window->width = (int32_t)LOWORD(lparam);
|
|
||||||
g_current_window->height = (int32_t)HIWORD(lparam);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
case WM_COMMAND:
|
|
||||||
if (g_current_window && HIWORD(wparam) == 0)
|
|
||||||
g_current_window->pending_menu_cmd = (int32_t)LOWORD(wparam);
|
|
||||||
return 0;
|
|
||||||
case WM_CLOSE:
|
|
||||||
if (g_current_window)
|
|
||||||
g_current_window->should_close = true;
|
|
||||||
return 0;
|
|
||||||
case WM_DESTROY:
|
|
||||||
PostQuitMessage(0);
|
|
||||||
return 0;
|
|
||||||
case WM_SYSCOMMAND:
|
|
||||||
if ((wparam & 0xfff0) == SC_KEYMENU)
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc)
|
|
||||||
{
|
|
||||||
WNDCLASSEXW wc = {};
|
|
||||||
wc.cbSize = sizeof(wc);
|
|
||||||
wc.style = CS_CLASSDC;
|
|
||||||
wc.lpfnWndProc = win32_wndproc;
|
|
||||||
wc.hInstance = GetModuleHandleW(nullptr);
|
|
||||||
wc.lpszClassName = L"autosample_wc";
|
|
||||||
RegisterClassExW(&wc);
|
|
||||||
|
|
||||||
int screen_w = GetSystemMetrics(SM_CXSCREEN);
|
|
||||||
int screen_h = GetSystemMetrics(SM_CYSCREEN);
|
|
||||||
int x = (screen_w - desc->width) / 2;
|
|
||||||
int y = (screen_h - desc->height) / 2;
|
|
||||||
|
|
||||||
RECT rect = { 0, 0, (LONG)desc->width, (LONG)desc->height };
|
|
||||||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
||||||
|
|
||||||
int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, nullptr, 0);
|
|
||||||
wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t));
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, wtitle, wchar_count);
|
|
||||||
|
|
||||||
HWND hwnd = CreateWindowExW(
|
|
||||||
0, wc.lpszClassName, wtitle,
|
|
||||||
WS_OVERLAPPEDWINDOW,
|
|
||||||
x, y,
|
|
||||||
rect.right - rect.left,
|
|
||||||
rect.bottom - rect.top,
|
|
||||||
nullptr, nullptr, wc.hInstance, nullptr
|
|
||||||
);
|
|
||||||
|
|
||||||
_freea(wtitle);
|
|
||||||
|
|
||||||
if (!hwnd)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
ShowWindow(hwnd, SW_SHOWDEFAULT);
|
|
||||||
UpdateWindow(hwnd);
|
|
||||||
|
|
||||||
PlatformWindow *window = new PlatformWindow();
|
|
||||||
window->hwnd = hwnd;
|
|
||||||
window->should_close = false;
|
|
||||||
window->width = desc->width;
|
|
||||||
window->height = desc->height;
|
|
||||||
|
|
||||||
g_current_window = window;
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_destroy_window(PlatformWindow *window)
|
|
||||||
{
|
|
||||||
if (!window) return;
|
|
||||||
|
|
||||||
if (window->hwnd) {
|
|
||||||
DestroyWindow(window->hwnd);
|
|
||||||
UnregisterClassW(L"autosample_wc", GetModuleHandleW(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_current_window == window)
|
|
||||||
g_current_window = nullptr;
|
|
||||||
|
|
||||||
delete window;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_poll_events(PlatformWindow *window)
|
|
||||||
{
|
|
||||||
MSG msg;
|
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessageW(&msg);
|
|
||||||
if (msg.message == WM_QUIT) {
|
|
||||||
window->should_close = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !window->should_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h)
|
|
||||||
{
|
|
||||||
if (w) *w = window->width;
|
|
||||||
if (h) *h = window->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *platform_get_native_handle(PlatformWindow *window)
|
|
||||||
{
|
|
||||||
return (void *)window->hwnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count)
|
|
||||||
{
|
|
||||||
HMENU menu_bar = CreateMenu();
|
|
||||||
|
|
||||||
for (int32_t i = 0; i < menu_count; i++) {
|
|
||||||
HMENU submenu = CreatePopupMenu();
|
|
||||||
|
|
||||||
for (int32_t j = 0; j < menus[i].item_count; j++) {
|
|
||||||
PlatformMenuItem *item = &menus[i].items[j];
|
|
||||||
if (!item->label) {
|
|
||||||
AppendMenuW(submenu, MF_SEPARATOR, 0, nullptr);
|
|
||||||
} else {
|
|
||||||
int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, nullptr, 0);
|
|
||||||
wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t));
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, item->label, -1, wlabel, wchar_count);
|
|
||||||
AppendMenuW(submenu, MF_STRING, (UINT_PTR)item->id, wlabel);
|
|
||||||
_freea(wlabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, nullptr, 0);
|
|
||||||
wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t));
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, wlabel, wchar_count);
|
|
||||||
AppendMenuW(menu_bar, MF_POPUP, (UINT_PTR)submenu, wlabel);
|
|
||||||
_freea(wlabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetMenu(window->hwnd, menu_bar);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t platform_poll_menu_command(PlatformWindow *window)
|
|
||||||
{
|
|
||||||
int32_t cmd = window->pending_menu_cmd;
|
|
||||||
window->pending_menu_cmd = 0;
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
struct Renderer;
|
|
||||||
|
|
||||||
struct RendererDesc {
|
|
||||||
void *window_handle = nullptr;
|
|
||||||
int32_t width = 1280;
|
|
||||||
int32_t height = 720;
|
|
||||||
int32_t frame_count = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
Renderer *renderer_create(RendererDesc *desc);
|
|
||||||
void renderer_destroy(Renderer *renderer);
|
|
||||||
bool renderer_begin_frame(Renderer *renderer);
|
|
||||||
void renderer_end_frame(Renderer *renderer);
|
|
||||||
void renderer_resize(Renderer *renderer, int32_t width, int32_t height);
|
|
||||||
366
src/renderer/renderer.jai
Normal file
366
src/renderer/renderer.jai
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
// autosample — DX12 renderer + ImGui integration
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
NUM_BACK_BUFFERS :: 2;
|
||||||
|
SRV_HEAP_SIZE :: 64;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// SRV Heap Allocator
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
SrvHeapAllocator :: struct {
|
||||||
|
heap: *ID3D12DescriptorHeap;
|
||||||
|
start_cpu: D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||||
|
start_gpu: D3D12_GPU_DESCRIPTOR_HANDLE;
|
||||||
|
increment: u32;
|
||||||
|
free_indices: [SRV_HEAP_SIZE] s32;
|
||||||
|
free_count: s32;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv_alloc_init :: (a: *SrvHeapAllocator, device: *ID3D12Device, heap: *ID3D12DescriptorHeap) {
|
||||||
|
a.heap = heap;
|
||||||
|
a.start_cpu = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
|
||||||
|
a.start_gpu = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);
|
||||||
|
a.increment = ID3D12Device_GetDescriptorHandleIncrementSize(device, .CBV_SRV_UAV);
|
||||||
|
|
||||||
|
desc := ID3D12DescriptorHeap_GetDesc(heap);
|
||||||
|
a.free_count = cast(s32) desc.NumDescriptors;
|
||||||
|
for i: 0..a.free_count-1 {
|
||||||
|
a.free_indices[i] = a.free_count - 1 - cast(s32) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srv_alloc_alloc :: (a: *SrvHeapAllocator, out_cpu: *D3D12_CPU_DESCRIPTOR_HANDLE, out_gpu: *D3D12_GPU_DESCRIPTOR_HANDLE) {
|
||||||
|
a.free_count -= 1;
|
||||||
|
idx := a.free_indices[a.free_count];
|
||||||
|
out_cpu.ptr = a.start_cpu.ptr + cast(u64)(idx * cast(s32) a.increment);
|
||||||
|
out_gpu.ptr = a.start_gpu.ptr + cast(u64)(idx * cast(s32) a.increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
srv_alloc_free :: (a: *SrvHeapAllocator, cpu: D3D12_CPU_DESCRIPTOR_HANDLE, gpu: D3D12_GPU_DESCRIPTOR_HANDLE) {
|
||||||
|
idx := cast(s32)((cpu.ptr - a.start_cpu.ptr) / cast(u64) a.increment);
|
||||||
|
a.free_indices[a.free_count] = idx;
|
||||||
|
a.free_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Frame Context
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
FrameContext :: struct {
|
||||||
|
command_allocator: *ID3D12CommandAllocator;
|
||||||
|
fence_value: u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Renderer state (module-level globals)
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
device: *ID3D12Device;
|
||||||
|
command_queue: *ID3D12CommandQueue;
|
||||||
|
swap_chain: *dxgi.IDXGISwapChain3;
|
||||||
|
swap_chain_waitable: HANDLE;
|
||||||
|
swap_chain_occluded: bool;
|
||||||
|
|
||||||
|
rtv_heap: *ID3D12DescriptorHeap;
|
||||||
|
srv_heap: *ID3D12DescriptorHeap;
|
||||||
|
srv_alloc: SrvHeapAllocator;
|
||||||
|
|
||||||
|
frames: [NUM_BACK_BUFFERS] FrameContext;
|
||||||
|
render_targets: [NUM_BACK_BUFFERS] *ID3D12Resource;
|
||||||
|
rtv_descriptors: [NUM_BACK_BUFFERS] D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||||
|
|
||||||
|
command_list: *ID3D12GraphicsCommandList;
|
||||||
|
fence: *ID3D12Fence;
|
||||||
|
fence_event: HANDLE;
|
||||||
|
fence_last_signaled: u64;
|
||||||
|
|
||||||
|
frame_index: u32;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// DX12 Setup
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
create_device :: () -> bool {
|
||||||
|
hr := D3D12CreateDevice(null, .D3D_FEATURE_LEVEL_11_0, *uid(ID3D12Device_UUID), xx *device);
|
||||||
|
if hr != S_OK {
|
||||||
|
print("D3D12CreateDevice failed: %\n", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_command_queue :: () -> bool {
|
||||||
|
desc: D3D12_COMMAND_QUEUE_DESC;
|
||||||
|
desc.Type = .DIRECT;
|
||||||
|
desc.Flags = .NONE;
|
||||||
|
desc.NodeMask = 1;
|
||||||
|
hr := ID3D12Device_CreateCommandQueue(device, *desc, *uid(ID3D12CommandQueue_UUID), xx *command_queue);
|
||||||
|
return hr == S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_descriptor_heaps :: () -> bool {
|
||||||
|
// RTV heap
|
||||||
|
{
|
||||||
|
desc: D3D12_DESCRIPTOR_HEAP_DESC;
|
||||||
|
desc.Type = .RTV;
|
||||||
|
desc.NumDescriptors = NUM_BACK_BUFFERS;
|
||||||
|
desc.Flags = .NONE;
|
||||||
|
desc.NodeMask = 1;
|
||||||
|
hr := ID3D12Device_CreateDescriptorHeap(device, *desc, *uid(ID3D12DescriptorHeap_UUID), xx *rtv_heap);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
|
||||||
|
rtv_size := ID3D12Device_GetDescriptorHandleIncrementSize(device, .RTV);
|
||||||
|
handle := ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);
|
||||||
|
for i: 0..NUM_BACK_BUFFERS-1 {
|
||||||
|
rtv_descriptors[i] = handle;
|
||||||
|
handle.ptr += cast(u64) rtv_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SRV heap
|
||||||
|
{
|
||||||
|
desc: D3D12_DESCRIPTOR_HEAP_DESC;
|
||||||
|
desc.Type = .CBV_SRV_UAV;
|
||||||
|
desc.NumDescriptors = SRV_HEAP_SIZE;
|
||||||
|
desc.Flags = .SHADER_VISIBLE;
|
||||||
|
desc.NodeMask = 1;
|
||||||
|
hr := ID3D12Device_CreateDescriptorHeap(device, *desc, *uid(ID3D12DescriptorHeap_UUID), xx *srv_heap);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
|
||||||
|
srv_alloc_init(*srv_alloc, device, srv_heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_frame_resources :: () -> bool {
|
||||||
|
for i: 0..NUM_BACK_BUFFERS-1 {
|
||||||
|
hr := ID3D12Device_CreateCommandAllocator(device, .DIRECT, *uid(ID3D12CommandAllocator_UUID), xx *frames[i].command_allocator);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
frames[i].fence_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr := ID3D12Device_CreateCommandList(device, 0, .DIRECT, frames[0].command_allocator, null, *uid(ID3D12GraphicsCommandList_UUID), xx *command_list);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
ID3D12GraphicsCommandList_Close(command_list);
|
||||||
|
|
||||||
|
hr = ID3D12Device_CreateFence(device, 0, .NONE, *uid(ID3D12Fence_UUID), xx *fence);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
|
||||||
|
fence_event = CreateEventW(null, 0, 0, null);
|
||||||
|
return fence_event != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_swap_chain :: () -> bool {
|
||||||
|
sd: dxgi.DXGI_SWAP_CHAIN_DESC1;
|
||||||
|
sd.BufferCount = NUM_BACK_BUFFERS;
|
||||||
|
sd.Width = 0;
|
||||||
|
sd.Height = 0;
|
||||||
|
sd.Format = .R8G8B8A8_UNORM;
|
||||||
|
sd.Flags = cast(u32) dxgi.DXGI_SWAP_CHAIN_FLAG.FRAME_LATENCY_WAITABLE_OBJECT;
|
||||||
|
sd.BufferUsage = .RENDER_TARGET_OUTPUT;
|
||||||
|
sd.SampleDesc.Count = 1;
|
||||||
|
sd.SwapEffect = .FLIP_DISCARD;
|
||||||
|
sd.Scaling = .STRETCH;
|
||||||
|
|
||||||
|
factory: *dxgi.IDXGIFactory4;
|
||||||
|
hr := dxgi.CreateDXGIFactory1(*uid(dxgi.IDXGIFactory4_UUID), xx *factory);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
defer IUnknown_Release(factory);
|
||||||
|
|
||||||
|
swap_chain1: *dxgi.IDXGISwapChain1;
|
||||||
|
hr = dxgi.IDXGIFactory2_CreateSwapChainForHwnd(factory, xx command_queue, hwnd, *sd, null, null, xx *swap_chain1);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
|
||||||
|
hr = IUnknown_QueryInterface(swap_chain1, *uid(dxgi.IDXGISwapChain3_UUID), xx *swap_chain);
|
||||||
|
IUnknown_Release(swap_chain1);
|
||||||
|
if hr != S_OK return false;
|
||||||
|
|
||||||
|
dxgi.IDXGIFactory_MakeWindowAssociation(factory, hwnd, xx dxgi.DXGI_MWA.NO_ALT_ENTER);
|
||||||
|
|
||||||
|
dxgi.IDXGISwapChain2_SetMaximumFrameLatency(swap_chain, NUM_BACK_BUFFERS);
|
||||||
|
swap_chain_waitable = dxgi.IDXGISwapChain2_GetFrameLatencyWaitableObject(swap_chain);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_render_targets :: () {
|
||||||
|
for i: 0..NUM_BACK_BUFFERS-1 {
|
||||||
|
dxgi.IDXGISwapChain_GetBuffer(swap_chain, cast(u32) i, *uid(ID3D12Resource_UUID), xx *render_targets[i]);
|
||||||
|
ID3D12Device_CreateRenderTargetView(device, render_targets[i], null, rtv_descriptors[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_render_targets :: () {
|
||||||
|
for i: 0..NUM_BACK_BUFFERS-1 {
|
||||||
|
if render_targets[i] {
|
||||||
|
IUnknown_Release(render_targets[i]);
|
||||||
|
render_targets[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_pending :: () {
|
||||||
|
fence_last_signaled += 1;
|
||||||
|
ID3D12CommandQueue_Signal(command_queue, fence, fence_last_signaled);
|
||||||
|
ID3D12Fence_SetEventOnCompletion(fence, fence_last_signaled, fence_event);
|
||||||
|
WaitForSingleObject(fence_event, 0xffff_ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// ImGui SRV allocator callbacks (C calling convention)
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
imgui_srv_alloc :: (info: *ImGui.ImplDX12_InitInfo, out_cpu: *D3D12_CPU_DESCRIPTOR_HANDLE, out_gpu: *D3D12_GPU_DESCRIPTOR_HANDLE) #c_call {
|
||||||
|
a := *srv_alloc;
|
||||||
|
a.free_count -= 1;
|
||||||
|
idx := a.free_indices[a.free_count];
|
||||||
|
out_cpu.ptr = a.start_cpu.ptr + cast(u64)(idx * cast(s32) a.increment);
|
||||||
|
out_gpu.ptr = a.start_gpu.ptr + cast(u64)(idx * cast(s32) a.increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui_srv_free :: (info: *ImGui.ImplDX12_InitInfo, cpu: D3D12_CPU_DESCRIPTOR_HANDLE, gpu: D3D12_GPU_DESCRIPTOR_HANDLE) #c_call {
|
||||||
|
a := *srv_alloc;
|
||||||
|
idx := cast(s32)((cpu.ptr - a.start_cpu.ptr) / cast(u64) a.increment);
|
||||||
|
a.free_indices[a.free_count] = idx;
|
||||||
|
a.free_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// ImGui Init
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
init_imgui :: () {
|
||||||
|
ImGui.CreateContext();
|
||||||
|
io := ImGui.GetIO();
|
||||||
|
io.ConfigFlags_ |= .NavEnableKeyboard;
|
||||||
|
io.ConfigFlags_ |= .DockingEnable;
|
||||||
|
|
||||||
|
ImGui.StyleColorsDark();
|
||||||
|
|
||||||
|
ImGui.ImplWin32_Init(hwnd);
|
||||||
|
|
||||||
|
init_info: ImGui.ImplDX12_InitInfo;
|
||||||
|
init_info.Device = device;
|
||||||
|
init_info.CommandQueue = command_queue;
|
||||||
|
init_info.NumFramesInFlight = NUM_BACK_BUFFERS;
|
||||||
|
init_info.RTVFormat = .R8G8B8A8_UNORM;
|
||||||
|
init_info.DSVFormat = .UNKNOWN;
|
||||||
|
init_info.SrvDescriptorHeap = srv_heap;
|
||||||
|
init_info.SrvDescriptorAllocFn = imgui_srv_alloc;
|
||||||
|
init_info.SrvDescriptorFreeFn = imgui_srv_free;
|
||||||
|
ImGui.ImplDX12_Init(*init_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Begin / End Frame
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
begin_frame :: () -> bool {
|
||||||
|
if swap_chain_occluded {
|
||||||
|
hr := dxgi.IDXGISwapChain_Present(xx swap_chain, 0, xx dxgi.DXGI_PRESENT.TEST);
|
||||||
|
if hr == xx dxgi.DXGI_STATUS_OCCLUDED {
|
||||||
|
Sleep(10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
swap_chain_occluded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.ImplDX12_NewFrame();
|
||||||
|
ImGui.ImplWin32_NewFrame();
|
||||||
|
ImGui.NewFrame();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_frame :: () {
|
||||||
|
ImGui.Render();
|
||||||
|
|
||||||
|
// Wait for next frame
|
||||||
|
fc := *frames[frame_index % NUM_BACK_BUFFERS];
|
||||||
|
if ID3D12Fence_GetCompletedValue(fence) < fc.fence_value {
|
||||||
|
ID3D12Fence_SetEventOnCompletion(fence, fc.fence_value, fence_event);
|
||||||
|
waitables: [2] HANDLE;
|
||||||
|
waitables[0] = swap_chain_waitable;
|
||||||
|
waitables[1] = fence_event;
|
||||||
|
WaitForMultipleObjects(2, waitables.data, cast(BOOL) 1, 0xffff_ffff);
|
||||||
|
} else {
|
||||||
|
WaitForSingleObject(swap_chain_waitable, 0xffff_ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
back_buffer_idx := dxgi.IDXGISwapChain3_GetCurrentBackBufferIndex(swap_chain);
|
||||||
|
|
||||||
|
ID3D12CommandAllocator_Reset(fc.command_allocator);
|
||||||
|
ID3D12GraphicsCommandList_Reset(command_list, fc.command_allocator, null);
|
||||||
|
|
||||||
|
// Transition to render target
|
||||||
|
barrier: D3D12_RESOURCE_BARRIER;
|
||||||
|
barrier.Type = .TRANSITION;
|
||||||
|
barrier.Flags = .NONE;
|
||||||
|
barrier.Transition.pResource = render_targets[back_buffer_idx];
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
barrier.Transition.StateBefore = .PRESENT;
|
||||||
|
barrier.Transition.StateAfter = .RENDER_TARGET;
|
||||||
|
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, *barrier);
|
||||||
|
|
||||||
|
clear_color := float.[0.1, 0.1, 0.1, 1.0];
|
||||||
|
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_descriptors[back_buffer_idx], *clear_color, 0, null);
|
||||||
|
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, *rtv_descriptors[back_buffer_idx], .FALSE, null);
|
||||||
|
ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, *srv_heap);
|
||||||
|
|
||||||
|
ImGui.ImplDX12_RenderDrawData(ImGui.GetDrawData(), command_list);
|
||||||
|
|
||||||
|
// Transition to present
|
||||||
|
barrier.Transition.StateBefore = .RENDER_TARGET;
|
||||||
|
barrier.Transition.StateAfter = .PRESENT;
|
||||||
|
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, *barrier);
|
||||||
|
ID3D12GraphicsCommandList_Close(command_list);
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
ID3D12CommandQueue_ExecuteCommandLists(command_queue, 1, xx *command_list);
|
||||||
|
fence_last_signaled += 1;
|
||||||
|
ID3D12CommandQueue_Signal(command_queue, fence, fence_last_signaled);
|
||||||
|
fc.fence_value = fence_last_signaled;
|
||||||
|
|
||||||
|
// Present
|
||||||
|
hr := dxgi.IDXGISwapChain_Present(xx swap_chain, 1, 0);
|
||||||
|
swap_chain_occluded = (hr == xx dxgi.DXGI_STATUS_OCCLUDED);
|
||||||
|
frame_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Resize
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
resize :: (width: u32, height: u32) {
|
||||||
|
if width == 0 || height == 0 return;
|
||||||
|
|
||||||
|
wait_for_pending();
|
||||||
|
cleanup_render_targets();
|
||||||
|
|
||||||
|
desc: dxgi.DXGI_SWAP_CHAIN_DESC1;
|
||||||
|
dxgi.IDXGISwapChain1_GetDesc1(swap_chain, *desc);
|
||||||
|
dxgi.IDXGISwapChain_ResizeBuffers(xx swap_chain, 0, width, height, desc.Format, desc.Flags);
|
||||||
|
|
||||||
|
create_render_targets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Cleanup
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
renderer_cleanup :: () {
|
||||||
|
wait_for_pending();
|
||||||
|
|
||||||
|
ImGui.ImplDX12_Shutdown();
|
||||||
|
ImGui.ImplWin32_Shutdown();
|
||||||
|
ImGui.DestroyContext();
|
||||||
|
|
||||||
|
cleanup_render_targets();
|
||||||
|
|
||||||
|
if swap_chain { IUnknown_Release(swap_chain); swap_chain = null; }
|
||||||
|
if swap_chain_waitable CloseHandle(swap_chain_waitable);
|
||||||
|
for i: 0..NUM_BACK_BUFFERS-1 {
|
||||||
|
if frames[i].command_allocator IUnknown_Release(frames[i].command_allocator);
|
||||||
|
}
|
||||||
|
if command_queue IUnknown_Release(command_queue);
|
||||||
|
if command_list IUnknown_Release(command_list);
|
||||||
|
if rtv_heap IUnknown_Release(rtv_heap);
|
||||||
|
if srv_heap IUnknown_Release(srv_heap);
|
||||||
|
if fence IUnknown_Release(fence);
|
||||||
|
if fence_event CloseHandle(fence_event);
|
||||||
|
if device IUnknown_Release(device);
|
||||||
|
}
|
||||||
@@ -1,436 +0,0 @@
|
|||||||
#include "renderer/renderer.h"
|
|
||||||
|
|
||||||
#include <d3d12.h>
|
|
||||||
#include <dxgi1_5.h>
|
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_win32.h"
|
|
||||||
#include "imgui_impl_dx12.h"
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
#define DX12_ENABLE_DEBUG_LAYER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
|
||||||
#include <dxgidebug.h>
|
|
||||||
#pragma comment(lib, "dxguid.lib")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NUM_BACK_BUFFERS 2
|
|
||||||
#define SRV_HEAP_SIZE 64
|
|
||||||
|
|
||||||
struct FrameContext {
|
|
||||||
ID3D12CommandAllocator *command_allocator;
|
|
||||||
UINT64 fence_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SrvHeapAllocator {
|
|
||||||
ID3D12DescriptorHeap *heap;
|
|
||||||
D3D12_DESCRIPTOR_HEAP_TYPE heap_type;
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE start_cpu;
|
|
||||||
D3D12_GPU_DESCRIPTOR_HANDLE start_gpu;
|
|
||||||
UINT increment;
|
|
||||||
int free_indices[SRV_HEAP_SIZE];
|
|
||||||
int free_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void srv_alloc_init(SrvHeapAllocator *a, ID3D12Device *device, ID3D12DescriptorHeap *heap)
|
|
||||||
{
|
|
||||||
a->heap = heap;
|
|
||||||
a->heap_type = heap->GetDesc().Type;
|
|
||||||
a->start_cpu = heap->GetCPUDescriptorHandleForHeapStart();
|
|
||||||
a->start_gpu = heap->GetGPUDescriptorHandleForHeapStart();
|
|
||||||
a->increment = device->GetDescriptorHandleIncrementSize(a->heap_type);
|
|
||||||
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
|
|
||||||
a->free_count = (int)desc.NumDescriptors;
|
|
||||||
for (int i = 0; i < a->free_count; i++)
|
|
||||||
a->free_indices[i] = a->free_count - 1 - i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void srv_alloc_alloc(SrvHeapAllocator *a, D3D12_CPU_DESCRIPTOR_HANDLE *out_cpu, D3D12_GPU_DESCRIPTOR_HANDLE *out_gpu)
|
|
||||||
{
|
|
||||||
int idx = a->free_indices[--a->free_count];
|
|
||||||
out_cpu->ptr = a->start_cpu.ptr + (idx * a->increment);
|
|
||||||
out_gpu->ptr = a->start_gpu.ptr + (idx * a->increment);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void srv_alloc_free(SrvHeapAllocator *a, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu)
|
|
||||||
{
|
|
||||||
(void)gpu;
|
|
||||||
int idx = (int)((cpu.ptr - a->start_cpu.ptr) / a->increment);
|
|
||||||
a->free_indices[a->free_count++] = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Renderer {
|
|
||||||
HWND hwnd;
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
int32_t frame_count;
|
|
||||||
UINT frame_index;
|
|
||||||
|
|
||||||
ID3D12Device *device;
|
|
||||||
ID3D12CommandQueue *command_queue;
|
|
||||||
IDXGISwapChain3 *swap_chain;
|
|
||||||
HANDLE swap_chain_waitable;
|
|
||||||
bool swap_chain_occluded;
|
|
||||||
bool tearing_support;
|
|
||||||
|
|
||||||
ID3D12DescriptorHeap *rtv_heap;
|
|
||||||
ID3D12DescriptorHeap *srv_heap;
|
|
||||||
SrvHeapAllocator srv_alloc;
|
|
||||||
|
|
||||||
FrameContext frames[NUM_BACK_BUFFERS];
|
|
||||||
ID3D12Resource *render_targets[NUM_BACK_BUFFERS];
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE rtv_descriptors[NUM_BACK_BUFFERS];
|
|
||||||
|
|
||||||
ID3D12GraphicsCommandList *command_list;
|
|
||||||
ID3D12Fence *fence;
|
|
||||||
HANDLE fence_event;
|
|
||||||
UINT64 fence_last_signaled;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool create_device(Renderer *r)
|
|
||||||
{
|
|
||||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
|
||||||
ID3D12Debug *debug = nullptr;
|
|
||||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
|
|
||||||
debug->EnableDebugLayer();
|
|
||||||
debug->Release();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&r->device)) != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
|
||||||
{
|
|
||||||
ID3D12InfoQueue *info_queue = nullptr;
|
|
||||||
if (SUCCEEDED(r->device->QueryInterface(IID_PPV_ARGS(&info_queue)))) {
|
|
||||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
|
|
||||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
|
|
||||||
info_queue->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool create_command_queue(Renderer *r)
|
|
||||||
{
|
|
||||||
D3D12_COMMAND_QUEUE_DESC desc = {};
|
|
||||||
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
||||||
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
||||||
desc.NodeMask = 1;
|
|
||||||
return r->device->CreateCommandQueue(&desc, IID_PPV_ARGS(&r->command_queue)) == S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool create_descriptor_heaps(Renderer *r)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
|
||||||
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
|
||||||
desc.NumDescriptors = NUM_BACK_BUFFERS;
|
|
||||||
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
|
||||||
desc.NodeMask = 1;
|
|
||||||
if (r->device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&r->rtv_heap)) != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
SIZE_T rtv_size = r->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle = r->rtv_heap->GetCPUDescriptorHandleForHeapStart();
|
|
||||||
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
|
|
||||||
r->rtv_descriptors[i] = handle;
|
|
||||||
handle.ptr += rtv_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
|
||||||
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
|
||||||
desc.NumDescriptors = SRV_HEAP_SIZE;
|
|
||||||
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
|
||||||
if (r->device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&r->srv_heap)) != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
srv_alloc_init(&r->srv_alloc, r->device, r->srv_heap);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool create_frame_resources(Renderer *r)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < r->frame_count; i++) {
|
|
||||||
if (r->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
IID_PPV_ARGS(&r->frames[i].command_allocator)) != S_OK)
|
|
||||||
return false;
|
|
||||||
r->frames[i].fence_value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r->device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
r->frames[0].command_allocator, nullptr, IID_PPV_ARGS(&r->command_list)) != S_OK)
|
|
||||||
return false;
|
|
||||||
if (r->command_list->Close() != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (r->device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&r->fence)) != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
r->fence_event = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
|
||||||
return r->fence_event != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool create_swap_chain(Renderer *r)
|
|
||||||
{
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 sd = {};
|
|
||||||
sd.BufferCount = NUM_BACK_BUFFERS;
|
|
||||||
sd.Width = 0;
|
|
||||||
sd.Height = 0;
|
|
||||||
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
|
|
||||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
||||||
sd.SampleDesc.Count = 1;
|
|
||||||
sd.SampleDesc.Quality = 0;
|
|
||||||
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
||||||
sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
|
||||||
sd.Scaling = DXGI_SCALING_STRETCH;
|
|
||||||
sd.Stereo = FALSE;
|
|
||||||
|
|
||||||
IDXGIFactory5 *factory = nullptr;
|
|
||||||
if (CreateDXGIFactory1(IID_PPV_ARGS(&factory)) != S_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
BOOL allow_tearing = FALSE;
|
|
||||||
factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
|
|
||||||
r->tearing_support = (allow_tearing == TRUE);
|
|
||||||
if (r->tearing_support)
|
|
||||||
sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
||||||
|
|
||||||
IDXGISwapChain1 *swap_chain1 = nullptr;
|
|
||||||
if (factory->CreateSwapChainForHwnd(r->command_queue, r->hwnd, &sd, nullptr, nullptr, &swap_chain1) != S_OK) {
|
|
||||||
factory->Release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (swap_chain1->QueryInterface(IID_PPV_ARGS(&r->swap_chain)) != S_OK) {
|
|
||||||
swap_chain1->Release();
|
|
||||||
factory->Release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r->tearing_support)
|
|
||||||
factory->MakeWindowAssociation(r->hwnd, DXGI_MWA_NO_ALT_ENTER);
|
|
||||||
|
|
||||||
swap_chain1->Release();
|
|
||||||
factory->Release();
|
|
||||||
|
|
||||||
r->swap_chain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
|
|
||||||
r->swap_chain_waitable = r->swap_chain->GetFrameLatencyWaitableObject();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void create_render_targets(Renderer *r)
|
|
||||||
{
|
|
||||||
for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
|
|
||||||
ID3D12Resource *back_buffer = nullptr;
|
|
||||||
r->swap_chain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
|
|
||||||
r->device->CreateRenderTargetView(back_buffer, nullptr, r->rtv_descriptors[i]);
|
|
||||||
r->render_targets[i] = back_buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cleanup_render_targets(Renderer *r)
|
|
||||||
{
|
|
||||||
for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
|
|
||||||
if (r->render_targets[i]) {
|
|
||||||
r->render_targets[i]->Release();
|
|
||||||
r->render_targets[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_for_pending(Renderer *r)
|
|
||||||
{
|
|
||||||
r->command_queue->Signal(r->fence, ++r->fence_last_signaled);
|
|
||||||
r->fence->SetEventOnCompletion(r->fence_last_signaled, r->fence_event);
|
|
||||||
WaitForSingleObject(r->fence_event, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FrameContext *wait_for_next_frame(Renderer *r)
|
|
||||||
{
|
|
||||||
FrameContext *fc = &r->frames[r->frame_index % r->frame_count];
|
|
||||||
if (r->fence->GetCompletedValue() < fc->fence_value) {
|
|
||||||
r->fence->SetEventOnCompletion(fc->fence_value, r->fence_event);
|
|
||||||
HANDLE waitables[] = { r->swap_chain_waitable, r->fence_event };
|
|
||||||
WaitForMultipleObjects(2, waitables, TRUE, INFINITE);
|
|
||||||
} else {
|
|
||||||
WaitForSingleObject(r->swap_chain_waitable, INFINITE);
|
|
||||||
}
|
|
||||||
return fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_imgui(Renderer *r)
|
|
||||||
{
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
||||||
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
ImGui_ImplWin32_Init(r->hwnd);
|
|
||||||
|
|
||||||
ImGui_ImplDX12_InitInfo init_info = {};
|
|
||||||
init_info.Device = r->device;
|
|
||||||
init_info.CommandQueue = r->command_queue;
|
|
||||||
init_info.NumFramesInFlight = r->frame_count;
|
|
||||||
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
|
|
||||||
init_info.SrvDescriptorHeap = r->srv_heap;
|
|
||||||
init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo *info, D3D12_CPU_DESCRIPTOR_HANDLE *out_cpu, D3D12_GPU_DESCRIPTOR_HANDLE *out_gpu) {
|
|
||||||
Renderer *r = (Renderer *)info->UserData;
|
|
||||||
srv_alloc_alloc(&r->srv_alloc, out_cpu, out_gpu);
|
|
||||||
};
|
|
||||||
init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo *info, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu) {
|
|
||||||
Renderer *r = (Renderer *)info->UserData;
|
|
||||||
srv_alloc_free(&r->srv_alloc, cpu, gpu);
|
|
||||||
};
|
|
||||||
init_info.UserData = r;
|
|
||||||
ImGui_ImplDX12_Init(&init_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
Renderer *renderer_create(RendererDesc *desc)
|
|
||||||
{
|
|
||||||
Renderer *r = new Renderer();
|
|
||||||
memset(r, 0, sizeof(*r));
|
|
||||||
|
|
||||||
r->hwnd = (HWND)desc->window_handle;
|
|
||||||
r->width = desc->width;
|
|
||||||
r->height = desc->height;
|
|
||||||
r->frame_count = desc->frame_count;
|
|
||||||
if (r->frame_count > NUM_BACK_BUFFERS) r->frame_count = NUM_BACK_BUFFERS;
|
|
||||||
|
|
||||||
if (!create_device(r)) goto fail;
|
|
||||||
if (!create_command_queue(r)) goto fail;
|
|
||||||
if (!create_descriptor_heaps(r)) goto fail;
|
|
||||||
if (!create_frame_resources(r)) goto fail;
|
|
||||||
if (!create_swap_chain(r)) goto fail;
|
|
||||||
create_render_targets(r);
|
|
||||||
|
|
||||||
init_imgui(r);
|
|
||||||
return r;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
renderer_destroy(r);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_destroy(Renderer *r)
|
|
||||||
{
|
|
||||||
if (!r) return;
|
|
||||||
|
|
||||||
wait_for_pending(r);
|
|
||||||
|
|
||||||
ImGui_ImplDX12_Shutdown();
|
|
||||||
ImGui_ImplWin32_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
|
|
||||||
cleanup_render_targets(r);
|
|
||||||
|
|
||||||
if (r->swap_chain) { r->swap_chain->SetFullscreenState(false, nullptr); r->swap_chain->Release(); }
|
|
||||||
if (r->swap_chain_waitable) CloseHandle(r->swap_chain_waitable);
|
|
||||||
for (int i = 0; i < r->frame_count; i++)
|
|
||||||
if (r->frames[i].command_allocator) r->frames[i].command_allocator->Release();
|
|
||||||
if (r->command_queue) r->command_queue->Release();
|
|
||||||
if (r->command_list) r->command_list->Release();
|
|
||||||
if (r->rtv_heap) r->rtv_heap->Release();
|
|
||||||
if (r->srv_heap) r->srv_heap->Release();
|
|
||||||
if (r->fence) r->fence->Release();
|
|
||||||
if (r->fence_event) CloseHandle(r->fence_event);
|
|
||||||
if (r->device) r->device->Release();
|
|
||||||
|
|
||||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
|
||||||
IDXGIDebug1 *dxgi_debug = nullptr;
|
|
||||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug)))) {
|
|
||||||
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
|
|
||||||
dxgi_debug->Release();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete r;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool renderer_begin_frame(Renderer *r)
|
|
||||||
{
|
|
||||||
if ((r->swap_chain_occluded && r->swap_chain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
|
|
||||||
|| IsIconic(r->hwnd))
|
|
||||||
{
|
|
||||||
Sleep(10);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
r->swap_chain_occluded = false;
|
|
||||||
|
|
||||||
ImGui_ImplDX12_NewFrame();
|
|
||||||
ImGui_ImplWin32_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_end_frame(Renderer *r)
|
|
||||||
{
|
|
||||||
ImGui::Render();
|
|
||||||
|
|
||||||
FrameContext *fc = wait_for_next_frame(r);
|
|
||||||
UINT back_buffer_idx = r->swap_chain->GetCurrentBackBufferIndex();
|
|
||||||
|
|
||||||
fc->command_allocator->Reset();
|
|
||||||
r->command_list->Reset(fc->command_allocator, nullptr);
|
|
||||||
|
|
||||||
D3D12_RESOURCE_BARRIER barrier = {};
|
|
||||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
||||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
|
||||||
barrier.Transition.pResource = r->render_targets[back_buffer_idx];
|
|
||||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
||||||
r->command_list->ResourceBarrier(1, &barrier);
|
|
||||||
|
|
||||||
const float clear_color[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
|
|
||||||
r->command_list->ClearRenderTargetView(r->rtv_descriptors[back_buffer_idx], clear_color, 0, nullptr);
|
|
||||||
r->command_list->OMSetRenderTargets(1, &r->rtv_descriptors[back_buffer_idx], FALSE, nullptr);
|
|
||||||
r->command_list->SetDescriptorHeaps(1, &r->srv_heap);
|
|
||||||
|
|
||||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), r->command_list);
|
|
||||||
|
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
|
||||||
r->command_list->ResourceBarrier(1, &barrier);
|
|
||||||
r->command_list->Close();
|
|
||||||
|
|
||||||
r->command_queue->ExecuteCommandLists(1, (ID3D12CommandList *const *)&r->command_list);
|
|
||||||
r->command_queue->Signal(r->fence, ++r->fence_last_signaled);
|
|
||||||
fc->fence_value = r->fence_last_signaled;
|
|
||||||
|
|
||||||
HRESULT hr = r->swap_chain->Present(1, 0);
|
|
||||||
r->swap_chain_occluded = (hr == DXGI_STATUS_OCCLUDED);
|
|
||||||
r->frame_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_resize(Renderer *r, int32_t width, int32_t height)
|
|
||||||
{
|
|
||||||
if (width <= 0 || height <= 0) return;
|
|
||||||
|
|
||||||
wait_for_pending(r);
|
|
||||||
cleanup_render_targets(r);
|
|
||||||
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 desc = {};
|
|
||||||
r->swap_chain->GetDesc1(&desc);
|
|
||||||
r->swap_chain->ResizeBuffers(0, (UINT)width, (UINT)height, desc.Format, desc.Flags);
|
|
||||||
|
|
||||||
create_render_targets(r);
|
|
||||||
|
|
||||||
r->width = width;
|
|
||||||
r->height = height;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user