diff --git a/CMakeLists.txt b/CMakeLists.txt index 55405eb..5dbcf55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,12 @@ if (APPLE) enable_language(OBJC OBJCXX) endif () +# 设置全局着色器输出目录 +set(SHADER_OUTPUT_DIR "${CMAKE_BINARY_DIR}/shaders" CACHE PATH "着色器编译输出路径") +# 设置全局着色器路径列表 +set(SHADER_SOURCE_DIRS "" CACHE STRING "着色器源文件路径列表") +mark_as_advanced(SHADER_OUTPUT_DIR) + set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE) set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE) set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE) diff --git a/cmake/compile_shaders.cmake b/cmake/compile_shaders.cmake index 6881004..8e40426 100644 --- a/cmake/compile_shaders.cmake +++ b/cmake/compile_shaders.cmake @@ -1,88 +1,16 @@ -function(compile_slang_shaders input_file stage entry_point) - # 保证输出目录存在 - if (NOT DEFINED SHADER_OUTPUT_DIR) - message(FATAL_ERROR "SHADER_OUTPUT_DIR not defined") - endif () - set(output_dir ${SHADER_OUTPUT_DIR}) +# 查找编译器和Python +find_program(PYTHON_EXECUTABLE python) +find_program(GLSLANG_VALIDATOR glslangValidator) +find_program(SLANG_COMPILER slangc) - # 获取输出文件的基本名称 - get_filename_component(filename ${input_file} NAME_WE) - - # 定义根据阶段确定的配置 - if(stage STREQUAL "vertex") - set(profile "vs_5_0") - elseif(stage STREQUAL "pixel") - set(profile "ps_5_0") - elseif (stage STREQUAL "comp") - set(profile "cs_5_0") - else() - message(FATAL_ERROR "Unsupported shader stage: ${stage}") - endif() - - # 初始化一个列表来存储输出文件 - set(output_files) - set(extra_args ${ARGN}) - # 如果是debug模式,添加调试标志 - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND extra_args "-g") - endif() - - # 为每个后台创建自定义命令 - if(LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGL_ES3 OR LLGL_BUILD_RENDERER_WEBGL) - set(output_file ${output_dir}/${filename}_${entry_point}.glsl) - add_custom_command( - OUTPUT ${output_file} - COMMAND slangc -target glsl -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_OPENGL=1 -DUV_FLIP=1 - DEPENDS ${input_file} - COMMENT "Compiling ${input_file} to GLSL (${stage}, ${entry_point})" - ) - list(APPEND output_files ${output_file}) - endif() - - if(LLGL_BUILD_RENDERER_DIRECT3D11 OR LLGL_BUILD_RENDERER_DIRECT3D12) - set(output_file ${output_dir}/${filename}_${entry_point}.dxbc) - add_custom_command( - OUTPUT ${output_file} - COMMAND slangc -target dxbc -profile ${profile} -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_DX=1 - DEPENDS ${input_file} - COMMENT "Compiling ${input_file} to DXBC (${stage}, ${entry_point}) with profile ${profile}" - ) - list(APPEND output_files ${output_file}) - endif() - - if(LLGL_BUILD_RENDERER_VULKAN) - set(output_file ${output_dir}/${filename}_${entry_point}.spirv) - add_custom_command( - OUTPUT ${output_file} - COMMAND slangc -target spirv -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_VULKAN=1 -DUV_FLIP=1 - DEPENDS ${input_file} - COMMENT "Compiling ${input_file} to SPIR-V (${stage}, ${entry_point})" - ) - list(APPEND output_files ${output_file}) - endif() - - if(LLGL_BUILD_RENDERER_METAL) - set(output_file ${output_dir}/${filename}_${entry_point}.metal) - add_custom_command( - OUTPUT ${output_file} - COMMAND slangc -target metal -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_METAL=1 - DEPENDS ${input_file} - COMMENT "Compiling ${input_file} to Metal Shading Language (${stage}, ${entry_point})" - ) - list(APPEND output_files ${output_file}) - endif() - - # 将输出文件添加到全局列表中 - set_property(GLOBAL APPEND PROPERTY ALL_SHADER_OUTPUTS ${output_files}) +function(shader_compile_target INPUT_DIR) + # 将路径拼接到全局着色器路径列表中 + list(APPEND SHADER_SOURCE_DIRS ${INPUT_DIR}) endfunction() -function(add_compile_shaders_target target_name before_target) - file(MAKE_DIRECTORY ${SHADER_OUTPUT_DIR}) - - get_property(ALL_SHADER_OUTPUTS GLOBAL PROPERTY ALL_SHADER_OUTPUTS) - add_custom_target(${target_name} ALL DEPENDS ${ALL_SHADER_OUTPUTS}) - # 将编译着色器的目标添加到指定目标之前 - add_dependencies(${before_target} ${target_name}) - # 将ALL_SHADER_OUTPUTS清空 - set_property(GLOBAL PROPERTY ALL_SHADER_OUTPUTS "") -endfunction() +# 添加自定义命令, 用于编译着色器, 调用scripts/compile_shaders.py +add_custom_target(compile_aorii_shaders + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/compile_shaders.py --input-dir ${SHADER_SOURCE_DIRS} --output-dir ${SHADER_OUTPUT_DIR} + COMMENT "Compiling shaders" + VERBATIM +) diff --git a/scripts/compile_shaders.py b/scripts/compile_shaders.py new file mode 100644 index 0000000..f7ec6e5 --- /dev/null +++ b/scripts/compile_shaders.py @@ -0,0 +1,100 @@ +import argparse +import os +import subprocess +import sys +import re + +def find_shader_files(input_dir, extensions): + for root, _, files in os.walk(input_dir): + for file in files: + if any(file.endswith(ext) for ext in extensions): + yield os.path.join(root, file) + +def compile_glsl(input_file, output_dir, glslang_validator): + try: + base = os.path.splitext(os.path.basename(input_file))[0] + ext = os.path.splitext(input_file)[1][1:] + relative_path = os.path.relpath(os.path.dirname(input_file), args.input_dir) + output_subdir = os.path.join(output_dir, relative_path) + output_file = os.path.join(output_subdir, f"{base}.{ext}.spv") + + os.makedirs(output_subdir, exist_ok=True) + + cmd = [glslang_validator, "-V", input_file, "-o", output_file] + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + print(f"Compiled GLSL: {input_file} -> {output_file}") + return True + except subprocess.CalledProcessError as e: + print(f"Error compiling {input_file}:") + print(e.stderr) + return False + except Exception as e: + print(f"Unexpected error with {input_file}: {str(e)}") + return False + +def find_slang_entries(input_file): + try: + with open(input_file, 'r') as f: + content = f.read() + pattern = r'\[shader\(\s*"(?:\w+)"\s*\)\]\s*\n\s*\w+\s+(\w+)\s*\(' + return list(set(re.findall(pattern, content))) + except Exception as e: + print(f"Error parsing {input_file}: {str(e)}") + return [] + +def compile_slang(input_file, output_dir, slangc): + try: + entries = find_slang_entries(input_file) + if not entries: + print(f"No entries found in {input_file}") + return False + + base = os.path.splitext(os.path.basename(input_file))[0] + relative_path = os.path.relpath(os.path.dirname(input_file), args.input_dir) + output_subdir = os.path.join(output_dir, relative_path) + os.makedirs(output_subdir, exist_ok=True) + + success = True + for entry in entries: + output_file = os.path.join(output_subdir, f"{base}_{entry}.spv") + cmd = [slangc, input_file, "-entry", entry, "-o", output_file, "-target", "spirv"] + try: + subprocess.run(cmd, check=True, capture_output=True, text=True) + print(f"Compiled Slang: {input_file}:{entry} -> {output_file}") + except subprocess.CalledProcessError as e: + print(f"Error compiling {input_file}:{entry}") + print(e.stderr) + success = False + return success + except Exception as e: + print(f"Unexpected error with {input_file}: {str(e)}") + return False + +def main(): + parser = argparse.ArgumentParser(description="Compile shaders to SPIR-V") + parser.add_argument("--input-dir", required=True, help="Input directory containing shaders") + parser.add_argument("--output-dir", required=True, help="Output directory for SPIR-V files") + parser.add_argument("--glslang", default="glslangValidator", help="Path to glslangValidator") + parser.add_argument("--slangc", default="slangc", help="Path to slangc") + args = parser.parse_args() + + glsl_ext = [".vert", ".frag", ".comp", ".geom", ".tesc", ".tese", ".glsl"] + slang_ext = [".slang"] + + all_success = True + + # Compile GLSL + for file in find_shader_files(args.input_dir, glsl_ext): + if not compile_glsl(file, args.output_dir, args.glang): + all_success = False + + # Compile Slang + for file in find_shader_files(args.input_dir, slang_ext): + if not compile_slang(file, args.output_dir, args.slangc): + all_success = False + + if not all_success: + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bce8a3e..eb20624 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -27,20 +27,7 @@ if (APPLE) endif () # 添加编译shader的自定义命令 -if (NOT DEFINED SHADER_OUTPUT_DIR) - set(SHADER_OUTPUT_DIR ${CMAKE_BINARY_DIR}/shaders CACHE PATH "Output directory for compiled shaders") - message(STATUS "SHADER_OUTPUT_DIR not defined, using default: ${SHADER_OUTPUT_DIR}") -endif () -function(compile_aorii_shader SHADER_FILE) - compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/${SHADER_FILE}.slang" "vertex" "vertex_main") - compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/${SHADER_FILE}.slang" "pixel" "pixel_main") -endfunction() -compile_aorii_shader(aorii_rect) -compile_aorii_shader(aorii_rounded_rect) -compile_aorii_shader(aorii_texture) -compile_aorii_shader(aorii_segment) -compile_aorii_shader(aorii_sdf_text) -add_compile_shaders_target("compile_aorii_shaders" ${PROJECT_NAME}) +shader_compile_target(${CMAKE_CURRENT_SOURCE_DIR}/shaders) # 如果需要编译example, 添加自定义命令用于拷贝shader文件 if (BUILD_EXAMPLE) @@ -50,10 +37,10 @@ if (BUILD_EXAMPLE) # 添加自定义命令, 当外部目标compile_aorii_shaders被编译时, 将${SOURCE_DIR}文件复制到${DEST_DIR} add_custom_command( - TARGET compile_aorii_shaders + TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR} COMMAND ${CMAKE_COMMAND} -E copy_directory ${SOURCE_DIR} ${DEST_DIR} COMMENT "Copying shaders to ${DEST_DIR}" ) -endif () \ No newline at end of file +endif () diff --git a/src/core/aorii.cpp b/src/core/aorii.cpp index fb57f5b..b445d37 100644 --- a/src/core/aorii.cpp +++ b/src/core/aorii.cpp @@ -46,12 +46,20 @@ namespace aorii { } int run(const init_info& in_init_info) { - if (!init(in_init_info.main_window_desc)) + try { + if (!init(in_init_info.main_window_desc)) + return -1; + while (!should_exit()) { + update(); + } + destroy(); + } catch (const std::exception& e) { + spdlog::error("运行时异常: {}", e.what()); + return -1; + } catch (...) { + spdlog::error("未知异常"); return -1; - while (!should_exit()) { - update(); } - destroy(); return 0; } diff --git a/src/core/renderer/renderer.cpp b/src/core/renderer/renderer.cpp index 011886a..02658af 100644 --- a/src/core/renderer/renderer.cpp +++ b/src/core/renderer/renderer.cpp @@ -162,6 +162,7 @@ bool aorii_renderer::init() { queue_create_info.setQueueCount(1); queue_create_info.setPQueuePriorities(&queue_priority); queue_create_infos.push_back(queue_create_info); + break; } vk::DeviceCreateInfo device_create_info{}; @@ -182,6 +183,7 @@ bool aorii_renderer::init() { } void aorii_renderer::destroy() { + spdlog::info("销毁渲染器"); main_device->destroy(); instance.destroy(); } @@ -203,26 +205,21 @@ const std::vector& aorii_renderer::get_compute_device() const { if (!compute_device.empty()) return compute_device; for (const auto& device: selector.compute_group) { - const float queue_priority = 1.0f; + constexpr float queue_priority = 1.0f; std::vector queue_create_infos; - const auto queue_families = device.getQueueFamilyProperties(); - for (uint32_t i = 0; i < queue_families.size(); ++i) { - const bool queue_compute_support = static_cast( - queue_families[i].queueFlags & vk::QueueFlagBits::eCompute); - if (queue_compute_support) { - vk::DeviceQueueCreateInfo queue_create_info{}; - queue_create_info.setQueueFamilyIndex(i); - queue_create_info.setQueueCount(1); - queue_create_info.setPQueuePriorities(&queue_priority); - queue_create_infos.push_back(queue_create_info); - } + const auto& families = device_selector::get_queue_families(device, vk::QueueFlagBits::eCompute); + for (const auto i: families) { + vk::DeviceQueueCreateInfo queue_create_info{}; + queue_create_info.setQueueFamilyIndex(i); + queue_create_info.setQueueCount(1); + queue_create_info.setPQueuePriorities(&queue_priority); + queue_create_infos.push_back(queue_create_info); } device_handle handle; vk::DeviceCreateInfo device_create_info{}; device_create_info.setQueueCreateInfos(queue_create_infos); handle.create_device(device, device_create_info); - spdlog::info("创建 Vulkan 计算设备: {}", handle.get_physical_device_name()); compute_device.push_back(handle); } return compute_device; diff --git a/src/core/renderer/renderer.h b/src/core/renderer/renderer.h index 9a670ce..a9aa8ff 100644 --- a/src/core/renderer/renderer.h +++ b/src/core/renderer/renderer.h @@ -19,6 +19,8 @@ public: void destroy_surface(const vk::SurfaceKHR& in_surface) const; device_handle get_main_device() const { return main_device; } const std::vector& get_compute_device() const; + + const auto& get_instance() const { return instance; } private: vk::Instance instance; device_selector selector; diff --git a/src/core/renderer/renderer_types.h b/src/core/renderer/renderer_types.h index d227004..0732ce1 100644 --- a/src/core/renderer/renderer_types.h +++ b/src/core/renderer/renderer_types.h @@ -1,5 +1,6 @@ #pragma once #include +#include struct device_handle { vk::PhysicalDevice physical_device; @@ -17,6 +18,7 @@ struct device_handle { void create_device(const vk::PhysicalDevice in_physical_device, const vk::DeviceCreateInfo& in_create_info) { physical_device = in_physical_device; device = physical_device.createDevice(in_create_info); + spdlog::info("创建 Vulkan 设备: {}", get_physical_device_name()); } void destroy_device() { if (device) { diff --git a/src/core/shader/aorii_rounded_rect.slang b/src/core/shader/aorii_rounded_rect.slang index 239af77..8f49ebc 100644 --- a/src/core/shader/aorii_rounded_rect.slang +++ b/src/core/shader/aorii_rounded_rect.slang @@ -14,6 +14,7 @@ struct PSInput { float4 color : COLOR; // 颜色 }; +[shader("vertex")] PSInput vertex_main(VSInput input) { PSInput output; @@ -32,6 +33,7 @@ float distance_from_rect_uv(float2 p, float corner_radius) { return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner_radius; } +[shader("pixel")] float4 pixel_main(PSInput input) : SV_Target { float2 p = uv_to_ndc(input.uv); diff --git a/src/core/shader/aorii_segment.slang b/src/core/shader/aorii_segment.slang index 5579038..4ba8cf6 100644 --- a/src/core/shader/aorii_segment.slang +++ b/src/core/shader/aorii_segment.slang @@ -6,6 +6,7 @@ struct PSInput { float4 color : COLOR; // 颜色 }; +[[vk::binding(0), vk::set(0)]] cbuffer segment_buffer : register(b0) { matrix transform; float2 pos_a; // 线段的起点 diff --git a/src/core/shader/aorii_texture.slang b/src/core/shader/aorii_texture.slang index e968c34..ec2eb1a 100644 --- a/src/core/shader/aorii_texture.slang +++ b/src/core/shader/aorii_texture.slang @@ -4,7 +4,7 @@ struct ParamBuffer { matrix transform; }; -ParameterBlock param_buffer : register(b0); +ParameterBlock param_buffer; struct PSInput { float4 position : SV_POSITION; // 裁剪空间坐标 @@ -12,8 +12,8 @@ struct PSInput { float4 color : COLOR; // 颜色 }; -Texture2D texture : register(t0); -SamplerState sampler : register(s0); +Texture2D texture; +SamplerState sampler; PSInput vertex_main(VSInput input) { diff --git a/src/core/shader/test.frag b/src/core/shader/test.frag new file mode 100644 index 0000000..3814cb5 --- /dev/null +++ b/src/core/shader/test.frag @@ -0,0 +1,9 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/src/core/shader/test.vert b/src/core/shader/test.vert new file mode 100644 index 0000000..789b7dc --- /dev/null +++ b/src/core/shader/test.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +out gl_PerVertex { + vec4 gl_Position; +}; +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} \ No newline at end of file diff --git a/src/core/shader/utils_vulkan.glsl b/src/core/shader/utils_vulkan.glsl new file mode 100644 index 0000000..9736dc3 --- /dev/null +++ b/src/core/shader/utils_vulkan.glsl @@ -0,0 +1,112 @@ +// Copyright Take Vos 2021. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +/** logical and. + */ +bvec4 and(bvec4 lhs, bvec4 rhs) +{ + return bvec4( + lhs.x && rhs.x, + lhs.y && rhs.y, + lhs.z && rhs.z, + lhs.w && rhs.w + ); +} + +/** Check if the rectangle contains the point. + * + * @param rectangle A axis-aligned rectangle encoded as left-bottom=(x,y), right-top=(z,w) + * @param point A 2D point + * @return True is the point is inside the rectangle. + */ +bool contains(vec4 rectangle, vec2 point) +{ + return greaterThanEqual(point.xyxy, rectangle) == bvec4(true, true, false, false); +} + +/** Convert coverage to a perceptional uniform alpha. + * + * This function takes into account the lightness of the full pixel, then + * determines based on this if the background is either black or white, or + * if linear conversion of coverage to alpha is needed. + * + * On black and white background we measure the target lightness of each sub-pixel + * then convert to target luminosity and eventually the alpha value. + * + * The alpha-component of the return value is calculated based on the full pixel + * lightness and from the green sub-pixel coverage. + * + * The full formula to convert coverage to alpha taking into account perceptional + * uniform lightness between foreground and background colors: + * ``` + * F = foreground color + * B = background color + * T = target color + * c = coverage + * a = alpha + * T = mix(sqrt(F), sqrt(B), c) ^ 2 + * + * a = (T - B) / (F - B) if F != B + * a = c otherwise + * ``` + * + * To simplify this formula and remove the division we fill in the foreground and background + * with black and white and the other way around: + * ``` + * a = c^2 if F == 1 and B == 0 + * a = 2c - c^2 if F == 0 and B == 1 + * ``` + * + * Now we mix based on the foreground color, expecting the background color to mirror. + * ``` + * a = mix(2c - c^2, c^2, F^2) if B^2 == 1 - F^2 + * ``` + * + * @param coverage The amount of coverage. Elements must be between 0.0 and 1.0 + * @param foreground_sq The sqrt of the foreground. Elements must be between 0.0 and 1.0 + * @return The alpha value for the red, blue, green, alpha color components. + */ +float coverage_to_alpha(float coverage, float sqrt_foreground) +{ + float coverage_sq = coverage * coverage; + float coverage_2 = coverage + coverage; + return mix(coverage_2 - coverage_sq, coverage_sq, sqrt_foreground); +} + +/** Convert coverage to a perceptional uniform alpha. + * + * @see coverage_to_alpha(float, float) + */ +vec4 coverage_to_alpha(vec4 coverage, vec4 sqrt_foreground) +{ + vec4 coverage_sq = coverage * coverage; + vec4 coverage_2 = coverage + coverage; + return mix(coverage_2 - coverage_sq, coverage_sq, sqrt_foreground); +} + +/** Multiply the alpha with the color. + * + * @param color The color+alpha without pre-multiplication. + * @return The color+alpha where the color is multiplied with the alpha. + */ +vec4 multiply_alpha(vec4 color) +{ + return vec4(color.rgb * color.a, color.a); +} + +/** Convert RGB to Y. + */ +float rgb_to_y(vec3 color) +{ + vec3 tmp = color * vec3(0.2126, 0.7152, 0.0722); + return tmp.r + tmp.g + tmp.b; +} + +/** Convert RGB to RGBY. + */ +vec4 rgb_to_rgby(vec3 color) +{ + return vec4(color, rgb_to_y(color)); +} + diff --git a/src/core/window/renderer_window.cpp b/src/core/window/renderer_window.cpp index fd1197d..b0a8487 100644 --- a/src/core/window/renderer_window.cpp +++ b/src/core/window/renderer_window.cpp @@ -47,11 +47,21 @@ renderer_window::~renderer_window() { } } -void renderer_window::set_title(const std::wstring& in_title) const { +void renderer_window::set_title(const std::string& in_title) const { + glfwSetWindowTitle(window, in_title.c_str()); +} + +void renderer_window::set_size(int in_width, int in_height) { + glfwSetWindowSize(window, in_width, in_height); + on_resize(in_width, in_height); } void renderer_window::close() const { + glfwSetWindowShouldClose(window, GLFW_TRUE); +} +void renderer_window::update() { + // swapchain->; } bool renderer_window::create_surface() { diff --git a/src/core/window/renderer_window.h b/src/core/window/renderer_window.h index be59125..cdaf3c2 100644 --- a/src/core/window/renderer_window.h +++ b/src/core/window/renderer_window.h @@ -30,7 +30,8 @@ public: explicit renderer_window(const window_desc& in_desc); virtual ~renderer_window(); - void set_title(const std::wstring& in_title) const; + void set_title(const std::string& in_title) const; + void set_size(int in_width, int in_height); void close() const; bool should_close() const { return glfwWindowShouldClose(window); } @@ -46,6 +47,8 @@ public: return vk::Extent2D(width, height); } + void update(); + bool create_surface(); [[nodiscard]] auto get_surface() const { return surface; } bool create_swap_chain(const renderer_swapchain::create_info& in_create_info);