217 lines
6.5 KiB
D

import std.stdio;
import dlib.aliases;
import dlib.util;
import dlibincludes;
import vulkan;
enum SDLWindowFlags : u64
{
FULLSCREEN = 0x0000000000000001, /**< window is in fullscreen mode */
OPENGL = 0x0000000000000002, /**< window usable with OpenGL context */
OCCLUDED = 0x0000000000000004, /**< window is occluded */
HIDDEN = 0x0000000000000008, /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow() is required for it to become visible */
BORDERLESS = 0x0000000000000010, /**< no window decoration */
RESIZABLE = 0x0000000000000020, /**< window can be resized */
MINIMIZED = 0x0000000000000040, /**< window is minimized */
MAXIMIZED = 0x0000000000000080, /**< window is maximized */
MOUSE_GRABBED = 0x0000000000000100, /**< window has grabbed mouse input */
INPUT_FOCUS = 0x0000000000000200, /**< window has input focus */
MOUSE_FOCUS = 0x0000000000000400, /**< window has mouse focus */
EXTERNAL = 0x0000000000000800, /**< window not created by SDL */
MODAL = 0x0000000000001000, /**< window is modal */
HIGH_PIXEL_DENSITY = 0x0000000000002000, /**< window uses high pixel density back buffer if possible */
MOUSE_CAPTURE = 0x0000000000004000, /**< window has mouse captured (unrelated to MOUSE_GRABBED) */
MOUSE_RELATIVE_MODE = 0x0000000000008000, /**< window has relative mode enabled */
ALWAYS_ON_TOP = 0x0000000000010000, /**< window should always be above others */
UTILITY = 0x0000000000020000, /**< window should be treated as a utility window, not showing in the task bar and window list */
TOOLTIP = 0x0000000000040000, /**< window should be treated as a tooltip and does not get mouse or keyboard focus, requires a parent window */
POPUP_MENU = 0x0000000000080000, /**< window should be treated as a popup menu, requires a parent window */
KEYBOARD_GRABBED = 0x0000000000100000, /**< window has grabbed keyboard input */
FILL_DOCUMENT = 0x0000000000200000, /**< window is in fill-document mode (Emscripten only), since SDL 3.4.0 */
VULKAN = 0x0000000010000000, /**< window usable for Vulkan surface */
METAL = 0x0000000020000000, /**< window usable for Metal view */
TRANSPARENT = 0x0000000040000000, /**< window with transparent buffer */
NOT_FOCUSABLE = 0x0000000080000000, /**< window should not be focusable */
}
alias SDL_WINDOW = SDLWindowFlags;
struct PxDisplay
{
MappedBuffer!(u32) mapped_buffer;
u32[] buffer;
UVec2 resolution;
Descriptor descriptor;
}
const u32 RESOLUTION_X = 640;
const u32 RESOLUTION_Y = 360;
const u32 BUFFER_SIZE = RESOLUTION_X * RESOLUTION_Y;
const UVec2 RESOLUTION = UVec2(RESOLUTION_X, RESOLUTION_Y);
struct PxWork
{
SDL_Window* window;
PxDisplay[FRAME_OVERLAP] displays;
Descriptor[FRAME_OVERLAP] draw_images;
PushConst pc;
DescSetLayout desc_set_layout;
DescSet[FRAME_OVERLAP] desc_sets;
PipelineLayout pipeline_layout;
Pipeline pipeline;
UVec2 dst_resolution;
bool[FRAME_OVERLAP] reload_image;
}
struct PushConst
{
UVec2 src_resolution;
}
PxWork px;
const u8[] SHADER_SRC = import("pixel.comp.spv");
void
CreateDrawImage(Descriptor* desc, UVec2 resolution)
{
CreateDescriptor(
desc,
DescInfo(
type: DT.StorageImage,
format: FMT.RGBA_F32,
usage: IU.Convert,
w: resolution.x,
h: resolution.y,
ch: 4,
binding: 0,
)
);
}
void main()
{
if(!SDL_Init(SDL_INIT_VIDEO))
{
Errf("Failed to init SDL [%s]", SDL_GetError());
assert(false);
}
px.window = SDL_CreateWindow("Video Game", 1920, 1080, SDL_WINDOW.VULKAN);
if(!px.window)
{
Errf("Failed to create SDL window [%s]", SDL_GetError());
assert(false);
}
VulkanBuildInfo build_info = {
application_name: "Video Game",
engine_name: "PxWork",
platform_handles: {
sdl: true,
sdl_window: px.window,
},
};
Init(build_info, MB(16), MB(8));
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.StorageImage, descriptorCount: 1, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Storage, descriptorCount: 1, stageFlags: SS.All },
];
px.desc_set_layout = CreateDescSetLayout(layout_bindings);
px.pipeline_layout = CreatePipelineLayout(px.desc_set_layout, PushConst.sizeof, true);
px.dst_resolution = UVec2(GetExtent());
px.pc.src_resolution = RESOLUTION;
foreach(i; 0 .. FRAME_OVERLAP)
{
px.displays[i].mapped_buffer = CreateMappedBuffer!(u32)(BT.Storage, BUFFER_SIZE);
px.displays[i].buffer = px.displays[i].mapped_buffer.data;
px.displays[i].resolution = RESOLUTION;
px.displays[i].descriptor.buffer = px.displays[i].mapped_buffer.base;
px.displays[i].descriptor.type = DT.Storage;
px.displays[i].descriptor.binding = 1;
CreateDrawImage(&px.draw_images[i], px.dst_resolution);
px.desc_sets[i] = AllocDescSet(px.desc_set_layout);
Write(px.desc_sets[i], &px.draw_images[i]);
Write(px.desc_sets[i], &px.displays[i].descriptor);
}
CompPipelineInfo pipeline_info = {
shader: cast(u8[])SHADER_SRC,
layout: px.pipeline_layout,
};
px.pipeline = CreateComputePipeline(&pipeline_info);
SetClearColors([0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
foreach(i; 0 .. FRAME_OVERLAP)
{
foreach(j; 0 .. RESOLUTION.y)
{
u32 cmp = j%2 == 0 ? 0 : 1;
foreach(k; 0 .. RESOLUTION.x)
{
px.displays[i].buffer[j*RESOLUTION.x + k] = k%2 == cmp;
}
}
}
u32 index = 0;
SDL_Event event;
bool run = true;
while(run)
{
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_EVENT_QUIT:
{
run = false;
} break;
default: break;
}
}
BeginFrame();
UVec2 extent = UVec2(GetExtent());
if(extent.v != px.dst_resolution.v)
{
px.dst_resolution = extent;
foreach(i; 0 .. FRAME_OVERLAP)
{
px.reload_image[index] = true;
}
}
if(px.reload_image[index])
{
Destroy(&px.draw_images[index]);
CreateDrawImage(&px.draw_images[index], px.dst_resolution);
Write(px.desc_sets[index], &px.draw_images[index]);
px.reload_image[index] = false;
}
PrepComputeImage(&px.draw_images[index]);
Bind(px.pipeline, px.desc_sets[index]);
PushConstants(px.pipeline, &px.pc);
Dispatch();
SubmitAndPresent(&px.draw_images[index]);
index = (index+1)%FRAME_OVERLAP;
}
}