圆角矩形着色器

This commit is contained in:
Nanako 2025-03-15 14:00:04 +08:00
parent 3479b9443b
commit 4727a47616
26 changed files with 3234 additions and 1668 deletions

10
.gitignore vendored
View File

@ -3,3 +3,13 @@
/.idea
/scripts/shader_paths.txt
/cache/shader_loader.h
#>fips
# this area is managed by fips, do not edit
.fips-*
fips-files/build/
fips-files/deploy/
*.pyc
.vscode/
.idea/
CMakeUserPresets.json
#<fips

View File

@ -1,13 +1,22 @@
cmake_minimum_required(VERSION 3.15)
#
# project: mirage
#
cmake_minimum_required(VERSION 3.21)
project(mirage)
project(mirage LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 26)
if (MSVC)
# MSVCC++
add_compile_options(/std:c++latest)
# utf-8
add_compile_options(/utf-8)
endif ()
if (WIN32)
# Windows
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-DUNICODE -D_UNICODE)
endif ()
set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE)
set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE)
@ -18,15 +27,12 @@ set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
#
set(SHADER_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resource/shaders" CACHE PATH "着色器编译输出路径")
set(MIRAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
include(cmake/retrieve_files.cmake)
include(cmake/detect_os.cmake)
include(cmake/compile_shaders.cmake)
include(cmake/configure_glfw_native.cmake)
include(cmake/config_macos.cmake)
include(cmake/compile_shaders.cmake)
# Debug,
if (CMAKE_BUILD_TYPE STREQUAL "Debug")

View File

@ -1,56 +1,75 @@
# Python
find_program(PYTHON_EXECUTABLE python3)
if (NOT PYTHON_EXECUTABLE)
find_program(PYTHON_EXECUTABLE python)
endif ()
if (NOT PYTHON_EXECUTABLE)
message(FATAL_ERROR "无法找到Python解释器")
endif ()
find_program(SLANG_COMPILER slangc REQUIRED)
include(CMakeParseArguments)
message(STATUS "Python解释器: ${PYTHON_EXECUTABLE}")
message(STATUS "SLANG编译器: ${SLANG_COMPILER}")
# Python
find_package(Python3 REQUIRED)
set(SHADER_PATH_FILE ${CMAKE_CURRENT_SOURCE_DIR}/scripts/shader_paths.txt)
set(SHADER_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cache)
set(SHADER_HEADER_FILE ${SHADER_HEADER_DIR}/shader_loader.h)
#
file(WRITE ${SHADER_HEADER_FILE} "")
#
set(SHADER_COMPILE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/tools/compile_shaders.py")
#
# cache.txt
set(SHADER_PATH_FILE ${CMAKE_CACHEFILE_DIR}/shader_paths.txt)
file(REMOVE ${SHADER_PATH_FILE})
file(WRITE ${SHADER_PATH_FILE} "")
function(shader_compile_target INPUT_DIR)
# scripts/shader_paths.txt
file(WRITE ${SHADER_PATH_FILE} ${INPUT_DIR})
#
function(add_mirage_shader_directory path)
# shader_paths.txt
file(APPEND ${SHADER_PATH_FILE} "${path}\n")
# , compile_shaders
file(GLOB_RECURSE SHADER_FILES "${path}/*.slang")
# shader
foreach(SHADER_FILE ${SHADER_FILES})
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${SHADER_FILE})
endforeach()
# 便使
set(SHADER_FILES_GLOBAL ${SHADER_FILES_GLOBAL} ${SHADER_FILES} PARENT_SCOPE)
endfunction()
set(SLANGC_ARGS "")
if (LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGLES3 OR LLGL_BUILD_RENDERER_WEBGL)
list(APPEND SLANGC_ARGS "--opengl")
endif ()
if (LLGL_BUILD_RENDERER_VULKAN)
list(APPEND SLANGC_ARGS "--vulkan")
endif ()
if (LLGL_BUILD_RENDERER_DIRECT3D11)
list(APPEND SLANGC_ARGS "--d3d11")
endif ()
if (LLGL_BUILD_RENDERER_DIRECT3D12)
list(APPEND SLANGC_ARGS "--d3d12")
endif ()
if (LLGL_BUILD_RENDERER_METAL)
list(APPEND SLANGC_ARGS "--metal")
endif ()
set(SHADER_COMPILE_ARGS "")
set(SHADER_SHDC "")
if (WIN32)
list(APPEND SHADER_COMPILE_ARGS "--hlsl")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc.exe)
elseif (APPLE)
list(APPEND SHADER_COMPILE_ARGS "--metal")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc)
else()
list(APPEND SHADER_COMPILE_ARGS "--glsl")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc)
endif()
# Debug, --debug
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND SLANGC_ARGS "--debug")
list(APPEND SHADER_COMPILE_ARGS "--debug")
endif ()
message(STATUS "着色器编译输出路径: ${SHADER_OUTPUT_DIR}")
# , , scripts/compile_shaders.py
add_custom_target(compile_shaders
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/compile_shaders.py --header ${SHADER_HEADER_FILE} --shader-list ${SHADER_PATH_FILE} --output-dir ${SHADER_OUTPUT_DIR} ${SLANGC_ARGS}
COMMENT ""
VERBATIM
# , tools/compile_shaders.py
add_custom_target(compile_shaders ALL
COMMAND ${Python3_EXECUTABLE} ${SHADER_COMPILE_SCRIPT}
--shdc ${SHADER_SHDC}
--shader_list ${SHADER_PATH_FILE}
${SHADER_COMPILE_ARGS}
COMMENT ""
VERBATIM
)
#
add_custom_target(force_recompile_shaders
COMMAND ${CMAKE_COMMAND} -E remove -f ${SHADER_PATH_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${SHADER_PATH_FILE}
COMMAND ${Python3_EXECUTABLE} ${SHADER_COMPILE_SCRIPT}
--shdc ${SHADER_SHDC}
--shader_list ${SHADER_PATH_FILE}
${SHADER_COMPILE_ARGS}
--force
COMMENT ""
VERBATIM
)
#
function(add_shader_dependencies target)
add_dependencies(${target} compile_shaders)
endfunction()

View File

@ -5,4 +5,6 @@ set(SRC_FILES "")
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES)
add_executable(${PROJECT_NAME} ${SRC_FILES})
target_link_libraries(${PROJECT_NAME} PRIVATE mirage_core)
target_link_options(${PROJECT_NAME} PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup")
if (WIN32)
target_link_options(${PROJECT_NAME} PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup")
endif()

View File

@ -7,14 +7,13 @@
int main(int argc, char* argv[]) {
mirage_app app;
app.init();
mirage_window window;
window.create_window(800, 600, L"Hello, World!");
window.show();
app.get_render_context()->setup_surface(&window, false);
while (!mirage_window::get_windows().empty()) {
mirage_window::poll_events();
}
app.init();
app.run();
return 0;
}

View File

@ -1,470 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import List, Tuple, Iterator
from pathlib import Path
import argparse
import subprocess
import sys
import re
# 设置控制台输出编码
# if sys.platform.startswith('win'):
# # Windows系统下设置
# sys.stdout.reconfigure(encoding='utf-8')
# sys.stderr.reconfigure(encoding='utf-8')
def print_utf8(message: str):
"""以UTF-8编码打印消息"""
print(message)
# 着色器类型和扩展名定义
SHADER_TYPES = {
'vertex': 'LLGL::ShaderType::Vertex',
'pixel': 'LLGL::ShaderType::Fragment',
'fragment': 'LLGL::ShaderType::Fragment',
'compute': 'LLGL::ShaderType::Compute',
'geometry': 'LLGL::ShaderType::Geometry',
'tess_control': 'LLGL::ShaderType::TessControl',
'tess_evaluation': 'LLGL::ShaderType::TessEvaluation'
}
SHADER_EXTENSIONS = {
'glsl': 'glsl',
'spirv': 'spirv',
'dxil': 'dxil',
'dxbc': 'dxbc',
'metallib': 'metallib',
'metal': 'metal',
'wgsl': 'wgsl'
}
# 不同目标平台的编译配置
TARGET_PROFILES = {
'glsl': ['-profile', 'glsl_460'],
'spirv': ['-profile', 'spirv_1_6'],
'dxbc': ['-profile', 'sm_5_0'],
'dxil': ['-profile', 'sm_6_6'],
'metallib': ['-capability', 'metallib'],
'metal': ['-capability', 'metal'],
}
class ShaderEntry:
"""着色器入口点信息"""
def __init__(self, name: str, shader_type: str):
self.name = name
self.shader_type = shader_type
class CompiledShaderInfo:
"""编译后的着色器信息"""
def __init__(self, output_file: str, base: str, entry: ShaderEntry):
self.output_file = output_file
self.base = base
self.entry = entry
if entry.shader_type == 'pixel':
self.entry.shader_type = 'fragment'
# 存放所有编译成功的着色器信息
compiled_shaders: List[CompiledShaderInfo] = []
def find_shader_files(input_dir: Path, extensions: List[str]) -> Iterator[Path]:
"""递归查找指定目录下的着色器文件"""
for file_path in Path(input_dir).rglob('*'):
if file_path.suffix in extensions:
yield file_path
def find_slang_entries(input_file: Path) -> List[ShaderEntry]:
"""从着色器文件中提取入口点函数名和类型"""
# 匹配 [shader("xxx")] 形式的着色器类型声明,以及后面的函数名
pattern = re.compile(
r'\[\s*shader\s*\(\s*"([^"]+)"\s*\)\s*\]\s*' # 匹配 [shader("xxx")]
r'(?:\[\s*[^\]]+\])*' # 可选:匹配其他属性如 [numthreads(8,8,1)]
r'\s*\w+\s+(\w+)\s*\(' # 匹配函数声明:返回类型 函数名(
)
try:
content = input_file.read_text(encoding='utf-8')
matches = pattern.findall(content)
print_utf8(f"**调试**: 在文件 {input_file} 中找到的匹配: {matches}")
entries = []
for shader_type, name in matches:
if shader_type in SHADER_TYPES:
entries.append(ShaderEntry(name, shader_type))
else:
print_utf8(f"**警告**: 未知的着色器类型 {shader_type}")
return entries
except Exception as e:
print_utf8(f"**错误**: 解析文件 {input_file} 失败: {e}")
return []
def get_shader_extension(build_type: str) -> str:
"""根据构建类型获取对应的着色器文件扩展名"""
return SHADER_EXTENSIONS.get(build_type, 'dat')
def create_compiler_command(
input_file: Path,
entry: ShaderEntry,
output_file: Path,
target_type: str,
args: argparse.Namespace
) -> List[str]:
"""生成着色器编译命令"""
cmd = [args.slangc,
str(input_file),
"-entry", entry.name,
"-o", str(output_file),
"-target", target_type,
"-g3" if args.debug else "-O3",
]
if target_type in TARGET_PROFILES:
cmd.extend(TARGET_PROFILES[target_type])
return cmd
def needs_recompile(input_file: Path, output_file: Path) -> bool:
"""检查是否需要重新编译着色器"""
if not output_file.exists():
return True
try:
return input_file.stat().st_mtime > output_file.stat().st_mtime
except OSError:
return True
def compile_shader(
input_file: Path,
target_types: List[Tuple[str, bool]],
output_dir: Path,
args: argparse.Namespace
) -> bool:
"""编译单个着色器文件"""
try:
entries = find_slang_entries(input_file)
if not entries:
print_utf8(f"**跳过**: {input_file} - 未找到着色器入口点")
return True
output_dir.mkdir(parents=True, exist_ok=True)
base = input_file.stem
success = True
for entry in entries:
compiled_shaders.append(CompiledShaderInfo(f"{base}_{entry.name}", base, entry))
for target_type, enabled in target_types:
if not enabled:
continue
for entry in entries:
output_file = output_dir / f"{base}_{entry.name}.{get_shader_extension(target_type)}"
if not needs_recompile(input_file, output_file):
print_utf8(f"**跳过**: {output_file} - 文件已是最新")
else:
cmd = create_compiler_command(input_file, entry, output_file, target_type, args)
try:
subprocess.run(cmd, check=True, capture_output=True, text=True)
print_utf8(f"**成功**: 编译 {input_file}:{entry.name} -> {output_file}")
except subprocess.CalledProcessError as e:
print_utf8(f"**错误**: 编译 {input_file}:{entry.name} 失败")
print_utf8(e.stderr)
success = False
continue
return success
except Exception as e:
print_utf8(f"**错误**: 处理 {input_file} 时发生异常: {e}")
return False
def generate_pipeline_header_preamble() -> List[str]:
"""Generate the header file preamble"""
return [
"#pragma once",
"",
"#include \"mirage.h\"",
"#include \"misc/mirage_type.h\"",
"#include <LLGL/LLGL.h>",
"#include <LLGL/Utils/VertexFormat.h>",
"#include <memory>",
"#include <string>",
"#include <vector>",
"#include <fstream>",
"#include <stdexcept>",
"",
"namespace generated_pipelines {",
"",
"// 辅助函数:加载着色器",
"inline auto LoadShader(",
" LLGL::RenderSystem* renderer,",
" const std::string& filename,",
" const LLGL::ShaderType type,",
" const char* entryPoint,",
" const LLGL::ShaderDescriptor& shaderDesc = {}) {",
"",
" // 根据渲染器类型选择着色器文件后缀",
" auto rendererID = renderer->GetRendererID();",
" std::string ext;",
" // 选择对应的文件扩展名",
" if (rendererID == LLGL::RendererID::OpenGL) {",
" ext = \".glsl\";",
" } else if (rendererID == LLGL::RendererID::Vulkan) {",
" ext = \".spirv\";",
" } else if (rendererID == LLGL::RendererID::Direct3D11) {",
" ext = \".dxbc\";",
" } else if (rendererID == LLGL::RendererID::Direct3D12) {",
" ext = \".dxil\";",
" } else if (rendererID == LLGL::RendererID::Metal) {",
" ext = \".metallib\";",
" } else {",
" ext = \".dat\";",
" }",
"",
" // 构造最终的文件名",
" std::string finalFilename = filename;",
" size_t pos = finalFilename.find_last_of('.');",
" if (pos != std::string::npos) {",
" finalFilename = finalFilename.substr(0, pos) + ext;",
" } else {",
" finalFilename += ext;",
" }",
" finalFilename = (mirage::get_shader_path() / finalFilename).string();",
"",
" // 读取着色器文件",
" std::vector<char> shaderData;",
" try {",
" std::ifstream file(finalFilename, std::ios::binary | std::ios::ate);",
" if (!file.is_open()) {",
" throw std::runtime_error(\"Failed to open shader file: \" + finalFilename);",
" }",
" size_t fileSize = static_cast<size_t>(file.tellg());",
" shaderData.resize(fileSize);",
" file.seekg(0);",
" file.read(shaderData.data(), fileSize);",
" } catch (const std::exception& e) {",
" throw std::runtime_error(\"Failed to read shader file: \" + std::string(e.what()));",
" }",
"",
" if (rendererID == LLGL::RendererID::OpenGL) {",
" // 添加终止符",
" shaderData.push_back('\\0');",
" }",
" // 创建着色器",
" LLGL::ShaderDescriptor desc = shaderDesc;",
" desc.source = shaderData.data();",
" desc.sourceSize = shaderData.size();",
" desc.entryPoint = rendererID == LLGL::RendererID::OpenGL ? \"main\" : entryPoint;",
" desc.type = type;",
" desc.profile = \"460\";",
" desc.sourceType = rendererID == LLGL::RendererID::OpenGL ? LLGL::ShaderSourceType::CodeString : LLGL::ShaderSourceType::BinaryBuffer;",
"",
" auto shader = renderer->CreateShader(desc);",
" if (auto report = shader->GetReport()) {",
" spdlog::error(\"Shader compilation report: {}\", report->GetText());",
" }",
" return mirage::shader_ptr(shader, mirage::llgl_deleter<LLGL::Shader>);",
"}",
""
"inline auto create_pipeline_layout(LLGL::RenderSystem* renderer, const LLGL::PipelineLayoutDescriptor& in_desc) {",
" auto pipelineLayout = renderer->CreatePipelineLayout(in_desc);",
" return mirage::pipeline_layout_ptr(pipelineLayout, mirage::llgl_deleter<LLGL::PipelineLayout>);",
"}",
]
def generate_compute_pipeline(header_lines: List[str], shader: CompiledShaderInfo):
"""Generate compute pipeline creation function"""
func_name = f"create_{shader.base}_{shader.entry.name}_pipeline"
header_lines.extend([
f"// 计算管线: {shader.base} - {shader.entry.name}",
f"inline auto {func_name}(LLGL::RenderSystem* renderer) {{",
" // 加载计算着色器",
f" auto computeShader = LoadShader(renderer, \"{shader.output_file.name}\", {SHADER_TYPES['compute']}, \"{shader.entry.name}\");",
"",
" // 创建管线布局",
" LLGL::PipelineLayoutDescriptor layoutDesc;",
" auto pipelineLayout = create_pipeline_layout(renderer, layoutDesc);",
"",
" // 创建计算管线",
" LLGL::ComputePipelineDescriptor pipelineDesc;",
" pipelineDesc.computeShader = computeShader.get();",
" pipelineDesc.pipelineLayout = pipelineLayout.get();",
"",
" auto pipeline = renderer->CreatePipelineState(pipelineDesc);",
" mirage::pipeline_info info;",
" info.pipeline_state = mirage::pipeline_state_ptr(pipeline, mirage::llgl_deleter<LLGL::PipelineState>);",
" info.pipeline_layout = pipelineLayout;",
" info.shaders = {computeShader};",
" return info;",
"}",
""
])
def generate_graphics_pipeline(header_lines: List[str], base: str, shaders: List[CompiledShaderInfo]):
"""Generate graphics pipeline creation function"""
func_name = f"create_{base}_pipeline"
header_lines.extend([
f"// 图形管线: {base}",
f"inline auto {func_name}(LLGL::RenderSystem* renderer, const LLGL::RenderPass* render_pass, const LLGL::PipelineLayoutDescriptor& in_pipeline_layout_desc, const LLGL::VertexFormat& vertex_format = mirage::create_vertex_format()) {{",
f" // 加载各个阶段的着色器",
f" LLGL::ShaderDescriptor vertexShaderDesc, fragShaderDesc;",
f" vertexShaderDesc.vertex.inputAttribs = vertex_format.attributes;",
])
SHADER_DESC_NAME = {
'vertex': 'vertexShaderDesc',
'pixel': 'fragShaderDesc',
'fragment': 'fragShaderDesc',
}
# Load all shader stages
for shader in shaders:
shader_type = shader.entry.shader_type
if shader_type in SHADER_TYPES:
header_lines.append(
f" auto {shader_type}Shader = LoadShader(renderer, "
f"\"{shader.output_file}\", {SHADER_TYPES[shader_type]}, "
f"\"{shader.entry.name}\", {SHADER_DESC_NAME[shader_type]});"
)
# Create pipeline layout and descriptor
header_lines.extend([
" // 创建管线布局",
" auto pipelineLayout = create_pipeline_layout(renderer, in_pipeline_layout_desc);",
"",
" // 创建图形管线",
" LLGL::GraphicsPipelineDescriptor pipelineDesc;",
])
# Set all shader stages
for shader in shaders:
shader_type = shader.entry.shader_type
if shader_type in SHADER_TYPES:
header_lines.append(f" pipelineDesc.{shader_type}Shader = {shader_type}Shader.get();")
# Set basic render states
header_lines.extend([
" pipelineDesc.pipelineLayout = pipelineLayout.get();",
"",
" // 设置基本渲染状态",
" pipelineDesc.renderPass = render_pass;",
" pipelineDesc.rasterizer.multiSampleEnabled = true;",
" pipelineDesc.blend.targets[0].blendEnabled = true;",
" pipelineDesc.depth.testEnabled = false;",
" pipelineDesc.depth.writeEnabled = false;",
"",
" auto pipeline = renderer->CreatePipelineState(pipelineDesc);",
])
# Finish function
header_lines.extend([
" mirage::pipeline_info info;",
" info.pipeline_state = mirage::pipeline_state_ptr(pipeline, mirage::llgl_deleter<LLGL::PipelineState>);",
" info.pipeline_layout = pipelineLayout;",
" info.shaders = {",
])
for shader in shaders:
shader_type = shader.entry.shader_type
if shader_type in SHADER_TYPES:
header_lines.append(f" {shader_type}Shader,")
header_lines.append(" };")
header_lines.extend([
" return info;",
"}",
""
])
def generate_pipeline_header(header_path: Path):
"""Generate the complete pipeline header file"""
header_lines = generate_pipeline_header_preamble()
# Group shaders by base name
shader_groups = {}
for shader in compiled_shaders:
if shader.base not in shader_groups:
shader_groups[shader.base] = []
shader_groups[shader.base].append(shader)
# Generate pipeline functions
for base, shaders in shader_groups.items():
has_compute = any(s.entry.shader_type == "compute" for s in shaders)
if has_compute:
for shader in shaders:
if shader.entry.shader_type == "compute":
generate_compute_pipeline(header_lines, shader)
else:
generate_graphics_pipeline(header_lines, base, shaders)
# Close namespace
header_lines.extend([
"} // namespace generated_pipelines",
""
])
try:
header_path.write_text("\n".join(header_lines), encoding="utf-8")
print_utf8(f"**成功**: 生成管线 C++ 头文件 {header_path}")
except Exception as e:
print_utf8(f"**错误**: 写入头文件 {header_path} 失败: {e}")
def main():
"""主函数:解析命令行参数并执行编译流程"""
if sys.platform.startswith('win'):
subprocess.run(['chcp', '65001'], shell=True)
parser = argparse.ArgumentParser(description="使用 slangc 编译着色器并生成 LLGL 渲染管线 C++ 头文件")
parser.add_argument("--shader-list", help="着色器列表文件路径")
parser.add_argument("--output-dir", help="输出目录")
parser.add_argument("--slangc", default="/Users/nanako/Documents/Slang/bin/slangc", help="slangc 编译器路径")
parser.add_argument("--debug", action="store_true", help="启用调试模式编译")
parser.add_argument("--opengl", action="store_true", help="编译 OpenGL 着色器")
parser.add_argument("--vulkan", action="store_true", help="编译 Vulkan 着色器")
parser.add_argument("--d3d11", action="store_true", help="编译 D3D11 着色器")
parser.add_argument("--d3d12", action="store_true", help="编译 D3D12 着色器")
parser.add_argument("--metal", action="store_true", help="编译 Metal 着色器")
parser.add_argument("--header", help="生成的头文件路径")
args = parser.parse_args()
target_types = [
['glsl', args.opengl],
['spirv', args.vulkan],
['dxbc', args.d3d11],
['dxil', args.d3d12],
['metallib', args.metal],
['metal', args.metal],
]
output_dir = Path(args.output_dir or "shaders")
shader_list = Path(args.shader_list or "shader_paths.txt")
try:
shader_paths = shader_list.read_text(encoding="utf-8").splitlines()
except Exception as e:
print_utf8(f"**错误**: 读取着色器列表文件 {shader_list} 失败: {e}")
sys.exit(1)
all_success = True
for shader_path in shader_paths:
shader_path = shader_path.strip()
if not shader_path:
continue
for file in find_shader_files(Path(shader_path), ['.slang']):
if not compile_shader(file, target_types, output_dir, args):
all_success = False
# 输出到shader_list所在目录
header_file = Path(args.header or shader_list.parent / "generated_pipelines.h")
generate_pipeline_header(header_file)
if not all_success:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -2,17 +2,6 @@ cmake_minimum_required(VERSION 3.15)
project(mirage_core LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 26)
if (MSVC)
# MSVCC++
add_compile_options(/std:c++latest)
# utf-8
add_compile_options(/utf-8)
endif ()
if (WIN32)
# Windows
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-DUNICODE -D_UNICODE)
endif()
find_package(Freetype REQUIRED)
find_package(Eigen3 REQUIRED)
@ -23,14 +12,14 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES)
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype Eigen3::Eigen)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
configure_glfw_native(${PROJECT_NAME})
add_os_definitions(${PROJECT_NAME})
# shader
add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/shaders)
add_shader_dependencies(${PROJECT_NAME})
if (WIN32)
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_D3D11=1)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d11 dxgi)
elseif (APPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_METAL=1)
target_compile_definitions(${PROJECT_NAME} PUBLIC -DSOKOL_D3D11)
elseif (UNIX)
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_GLCORE=1)
target_compile_definitions(${PROJECT_NAME} PUBLIC -DSOKOL_GLCORE33)
endif ()

View File

@ -1,18 +1,22 @@
#pragma once
#include "sokol/sokol_gfx.h"
#include <memory>
#include "misc/mirage_type.h"
#include <functional>
#include <map>
class mirage_window;
class mirage_render_context {
public:
virtual ~mirage_render_context() = default;
virtual bool init() { return false; }
virtual void cleanup() { }
virtual void tick(const duration_type& in_delta) = 0;
virtual sg_environment get_environment() = 0;
virtual bool setup_surface(mirage_window* in_window, bool in_hdr) { return false; }
private:
};
std::unique_ptr<mirage_render_context> mirage_create_render_context();
mirage_render_context* mirage_create_render_context();

View File

@ -1,3 +1,29 @@
#include "render_window.h"
Eigen::Matrix4f mirage_window::create_screen_to_dci_matrix(float in_screen_width, float in_screen_height) {
// 创建一个单位矩阵
Eigen::Matrix4f matrix = Eigen::Matrix4f::Identity();
// 缩放因子
const float scale_x = 2.0f / in_screen_width;
const float scale_y = -2.0f / in_screen_height; // Y轴翻转因为窗口坐标系Y轴向下
// 平移因子
constexpr float translate_x = -1.0f;
constexpr float translate_y = 1.0f;
// 设置缩放
matrix(0, 0) = scale_x;
matrix(1, 1) = scale_y;
// 设置平移
matrix(0, 3) = translate_x;
matrix(1, 3) = translate_y;
return matrix;
}
Eigen::Matrix4f mirage_window::create_screen_to_dci_matrix() const {
const auto size = get_window_frame_size();
return create_screen_to_dci_matrix(size.x(), size.y());
}

View File

@ -2,6 +2,23 @@
#include "windows/windows_render_context.h"
#include <Eigen/Eigen>
class mirage_window;
struct mirage_window_state {
virtual ~mirage_window_state() {
clear();
}
sg_buffer buffer;
sg_swapchain swapchain;
sg_bindings bindings;
sg_pipeline pipeline;
bool vsync = true;
virtual void clear() {}
virtual void present() {}
virtual void resize(const Eigen::Vector2i& size) {}
};
class mirage_window {
public:
bool create_window(int width, int height, const wchar_t* title);
@ -18,6 +35,12 @@ public:
void move(const Eigen::Vector2i& pos) { move(pos.x(), pos.y()); }
[[nodiscard]] Eigen::Vector2i get_window_size() const;
[[nodiscard]] Eigen::Vector2i get_window_position() const;
[[nodiscard]] Eigen::Vector2i get_window_frame_size() const;
// 创建从屏幕坐标到DCI坐标的正交投影矩阵
[[nodiscard]] static Eigen::Matrix4f create_screen_to_dci_matrix(float in_screen_width, float in_screen_height);
[[nodiscard]] Eigen::Matrix4f create_screen_to_dci_matrix() const;
[[nodiscard]] void* get_window_handle() const;
// style functions
@ -34,6 +57,10 @@ public:
[[nodiscard]] bool close_requested() const { return close_request; }
static bool poll_events();
static const std::vector<mirage_window*>& get_windows();
void on_resize(int width, int height);
std::unique_ptr<mirage_window_state> state;
private:
void* window_handle{};
bool close_request = false;

View File

@ -5,141 +5,216 @@
#include "windows_render_context.h"
#include <iostream>
#include <ranges>
#include "core/render_window.h"
#include <windows.h>
mirage_windows_render_context::~mirage_windows_render_context() {
cleanup();
}
#include "windows_window_state.h"
#include "misc/scope_exit.h"
#include "shaders/test.hlsl.h"
#include "shaders/mirage_rounded_rect.hlsl.h"
bool mirage_windows_render_context::init() {
try {
// 定义支持的特性级别(从高到低排序)
D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
windows_mirage_render_context::~windows_mirage_render_context() { cleanup(); }
// 设置设备创建标志
UINT device_flags = 0;
#if DEBUG
device_flags |= D3D11_CREATE_DEVICE_DEBUG; // 在Debug模式下启用调试层
#endif
bool windows_mirage_render_context::init() {
try {
// 定义支持的特性级别(从高到低排序)
D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
// 定义要尝试的驱动类型数组
constexpr D3D_DRIVER_TYPE driver_types[] = {
D3D_DRIVER_TYPE_HARDWARE, // 首选硬件加速
D3D_DRIVER_TYPE_WARP, // 其次是WARP软件渲染器
D3D_DRIVER_TYPE_REFERENCE // 最后是参考软件渲染器
};
// 设置设备创建标志
UINT device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED; // BGRA支持和单线程模式
#if DEBUG
device_flags |= D3D11_CREATE_DEVICE_DEBUG; // 在Debug模式下启用调试层
#endif
// 尝试按优先级创建设备
HRESULT hr = E_FAIL;
D3D_DRIVER_TYPE used_driver_type = D3D_DRIVER_TYPE_UNKNOWN;
// 定义要尝试的驱动类型数组
constexpr D3D_DRIVER_TYPE driver_types[] = {
D3D_DRIVER_TYPE_HARDWARE,
// 首选硬件加速
D3D_DRIVER_TYPE_WARP,
// 其次是WARP软件渲染器
D3D_DRIVER_TYPE_REFERENCE // 最后是参考软件渲染器
};
for (const auto& driver_type : driver_types) {
hr = D3D11CreateDevice(
nullptr, // 使用默认适配器
driver_type, // 驱动类型
nullptr, // 软件栅格化模块句柄(仅用于软件设备)
device_flags, // 设备创建标志
feature_levels, // 特性级别数组
ARRAYSIZE(feature_levels), // 特性级别数量
D3D11_SDK_VERSION, // SDK版本
&device, // 输出设备
&feature_level, // 输出获取的特性级别
&device_context // 输出设备上下文
);
// 尝试按优先级创建设备
HRESULT hr = E_FAIL;
D3D_DRIVER_TYPE used_driver_type = D3D_DRIVER_TYPE_UNKNOWN;
// 使用 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2推荐
if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
std::cout << "mirage: " << "DPI awareness set to per-monitor aware v2" << std::endl;
}
if (SUCCEEDED(hr)) {
used_driver_type = driver_type;
break;
}
}
for (const auto& driver_type: driver_types) {
hr = D3D11CreateDevice(
nullptr,
// 使用默认适配器
driver_type,
// 驱动类型
nullptr,
// 软件栅格化模块句柄(仅用于软件设备)
device_flags,
// 设备创建标志
feature_levels,
// 特性级别数组
ARRAYSIZE(feature_levels),
// 特性级别数量
D3D11_SDK_VERSION,
// SDK版本
&device,
// 输出设备
&feature_level,
// 输出获取的特性级别
&device_context // 输出设备上下文
);
// 检查是否成功创建设备
if (FAILED(hr)) {
std::cerr << "Failed to create D3D11 device with any driver type. HRESULT: 0x"
<< std::hex << hr << std::dec << std::endl;
cleanup();
return false;
}
if (SUCCEEDED(hr)) {
used_driver_type = driver_type;
break;
}
}
// 输出使用设备
DXGI_ADAPTER_DESC adapter_desc;
// 检查是否成功创建设备
if (FAILED(hr)) {
std::cerr << "mirage: " << "Failed to create D3D11 device with any driver type. HRESULT: 0x"
<< std::hex << hr << std::dec << std::endl;
cleanup();
return false;
}
// 获取DXGI工厂以便后续创建交换链
// 输出使用设备
DXGI_ADAPTER_DESC adapter_desc;
// 获取DXGI工厂以便后续创建交换链
IDXGIAdapter* dxgi_adapter = nullptr;
IDXGIDevice* dxgi_device = nullptr;
hr = device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
if (SUCCEEDED(hr)) {
hr = dxgi_device->GetAdapter(&dxgi_adapter);
if (SUCCEEDED(hr)) {
hr = dxgi_adapter->GetDesc(&adapter_desc);
if (FAILED(hr)) {
std::wcerr << L"Failed to get DXGI Adapter description" << std::endl;
}
hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&dxgi_factory));
if (FAILED(hr)) {
dxgi_factory = nullptr;
}
dxgi_adapter->Release();
}
dxgi_device->Release();
}
IDXGIDevice* dxgi_device = nullptr;
hr = device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
if (SUCCEEDED(hr)) {
hr = dxgi_device->GetAdapter(&dxgi_adapter);
if (SUCCEEDED(hr)) {
hr = dxgi_adapter->GetDesc(&adapter_desc);
if (FAILED(hr)) { std::wcerr << L"mirage: " << L"Failed to get DXGI Adapter description" << std::endl; }
hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&dxgi_factory));
if (FAILED(hr)) { dxgi_factory = nullptr; }
dxgi_adapter->Release();
}
dxgi_device->Release();
}
if (!dxgi_factory) {
std::cerr << "Failed to get DXGI Factory" << std::endl;
cleanup();
return false;
}
if (!dxgi_factory) {
std::cerr << "Failed to get DXGI Factory" << std::endl;
cleanup();
return false;
}
// 输出初始化成功信息
std::cout << "D3D11 device created successfully" << std::endl;
std::wcout << L"Using adapter: " << adapter_desc.Description << std::endl;
// 输出初始化成功信息
std::cout << "mirage: " << "D3D11 device created successfully" << std::endl;
std::wcout << L"mirage: " << L"Using adapter: " << adapter_desc.Description << std::endl;
// 输出驱动类型信息
auto driver_type_str = "Unknown";
switch (used_driver_type) {
case D3D_DRIVER_TYPE_HARDWARE: driver_type_str = "Hardware"; break;
case D3D_DRIVER_TYPE_WARP: driver_type_str = "WARP"; break;
case D3D_DRIVER_TYPE_REFERENCE: driver_type_str = "Reference"; break;
default: ;
}
std::cout << "Using driver type: " << driver_type_str << std::endl;
// 输出驱动类型信息
auto driver_type_str = "Unknown";
switch (used_driver_type) {
case D3D_DRIVER_TYPE_HARDWARE:
driver_type_str = "Hardware";
break;
case D3D_DRIVER_TYPE_WARP:
driver_type_str = "WARP";
break;
case D3D_DRIVER_TYPE_REFERENCE:
driver_type_str = "Reference";
break;
default: ;
}
std::cout << "mirage: " << "Using driver type: " << driver_type_str << std::endl;
// 输出特性级别信息
auto feature_level_str = "Unknown";
switch (feature_level) {
case D3D_FEATURE_LEVEL_11_1: feature_level_str = "11.1"; break;
case D3D_FEATURE_LEVEL_11_0: feature_level_str = "11.0"; break;
case D3D_FEATURE_LEVEL_10_1: feature_level_str = "10.1"; break;
case D3D_FEATURE_LEVEL_10_0: feature_level_str = "10.0"; break;
default: ;
}
std::cout << "Using feature level: " << feature_level_str << std::endl;
// 输出特性级别信息
auto feature_level_str = "Unknown";
switch (feature_level) {
case D3D_FEATURE_LEVEL_11_1:
feature_level_str = "11.1";
break;
case D3D_FEATURE_LEVEL_11_0:
feature_level_str = "11.0";
break;
case D3D_FEATURE_LEVEL_10_1:
feature_level_str = "10.1";
break;
case D3D_FEATURE_LEVEL_10_0:
feature_level_str = "10.0";
break;
default: ;
}
std::cout << "mirage: " << "Using feature level: " << feature_level_str << std::endl;
return true;
}
catch (const std::exception& e) {
std::cerr << "Exception during D3D11 initialization: " << e.what() << std::endl;
cleanup();
return false;
}
return true;
} catch (const std::exception& e) {
std::cerr << "mirage: " << "Exception during D3D11 initialization: " << e.what() << std::endl;
cleanup();
return false;
}
}
// 资源清理函数
void mirage_windows_render_context::cleanup() {
// 安全释放COM接口
if (device_context) { device_context->Release(); device_context = nullptr; }
if (device) { device->Release(); device = nullptr; }
if (dxgi_factory) { dxgi_factory->Release(); dxgi_factory = nullptr; }
void windows_mirage_render_context::cleanup() {
mirage_render_context::cleanup();
// 安全释放COM接口
sg_shutdown();
if (device_context) {
device_context->Release();
device_context = nullptr;
}
if (device) {
device->Release();
device = nullptr;
}
if (dxgi_factory) {
dxgi_factory->Release();
dxgi_factory = nullptr;
}
}
void windows_mirage_render_context::tick(const duration_type& in_delta) {
const auto& windows = mirage_window::get_windows();
for (const auto& window: windows) {
auto& window_state = window->state;
sg_pass pass{};
pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
pass.action.colors[0].store_action = SG_STOREACTION_STORE;
pass.action.colors[0].clear_value = { 0.f, 0.f, 0.f, 1.0f };
sg_environment mirage_windows_render_context::get_environment() {
pass.action.depth.load_action = SG_LOADACTION_CLEAR;
pass.action.depth.store_action = SG_STOREACTION_DONTCARE;
pass.action.depth.clear_value = 1.0f;
pass.swapchain = window_state->swapchain;
sg_begin_pass(pass);
sg_apply_pipeline(pip);
sg_apply_viewport(0, 0, window_state->swapchain.width, window_state->swapchain.height, true);
sg_bindings bindings{};
bindings.vertex_buffers[0] = vertex_buffer;
bindings.index_buffer = index_buffer;
sg_apply_bindings(bindings);
const auto& matrix = window->create_screen_to_dci_matrix();
sg_apply_uniforms(0, SG_RANGE(matrix));
sg_draw(0, 12, 2);
sg_end_pass();
sg_commit();
window_state->present();
}
}
sg_environment windows_mirage_render_context::get_environment() {
return {
.d3d11 = {
.device = device,
@ -148,35 +223,80 @@ sg_environment mirage_windows_render_context::get_environment() {
};
}
bool mirage_windows_render_context::setup_surface(mirage_window* in_window, bool in_hdr) {
const auto& window_size = in_window->get_window_size();
bool windows_mirage_render_context::setup_surface(mirage_window* in_window, bool in_hdr) {
auto state = std::make_unique<windows_window_state>();
if (!state->init(device, dxgi_factory, in_window, in_hdr)) { return false; }
const auto format = in_hdr ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
shader = sg_make_shader(get_mirage_rounded_rect_shader_desc());
// 创建D3D11渲染目标视图
IDXGISwapChain* swap_chain = nullptr;
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {
.BufferDesc = {
.Width = static_cast<UINT>(window_size.x()),
.Height = static_cast<UINT>(window_size.y()),
.RefreshRate = {
.Numerator = 60,
.Denominator = 1
},
.Format = format,
},
.SampleDesc = {
.Count = 1,
.Quality = 0
},
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferCount = 1,
.OutputWindow = nullptr,
.Windowed = TRUE
sg_pipeline_desc pipeline_desc = get_mirage_rounded_rect_pipeline_desc(shader, state->swapchain);
pip = sg_make_pipeline(pipeline_desc);
mirage_triangle_t triangles[4];
// 创建顶点缓冲区(一个矩形)
std::vector<mirage_vertex_t> vertices = {
// x, y, r, g, b, a
{ { 0.f, 0.f }, { 0.f, 0.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
// 左上 - 黄色
{ { 100.f, 0.f }, { 1.f, 0.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
// 右上 - 蓝色
{ { 0.f, 100.f }, { 0.f, 1.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
// 左下 - 红色
{ { 100.f, 100.f }, { 1.f, 1.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
// 右下 - 绿色
{ { 200.f, 200.f }, { 0.f, 0.f }, { 0.0f, 1.0f, 1.0f, 1.0f } },
// 左上 - 黄色
{ { 300.f, 200.f }, { 1.f, 0.f }, { 1.0f, 0.0f, 1.0f, 1.0f } },
// 右上 - 蓝色
{ { 200.f, 300.f }, { 0.f, 1.f }, { 1.0f, 0.0f, 1.0f, 1.0f } },
// 左下 - 红色
{ { 300.f, 300.f }, { 1.f, 1.f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
// 右下 - 绿色
};
for (auto& v: vertices) {
v.param_a.a = 100;
v.param_a.b = 100;
v.param_b.a = 0;
v.param_b.b = 5;
v.param_b.c = 10;
v.param_b.d = 20;
}
// 三角形索引不变
triangles[0].indices[0] = 0;
triangles[0].indices[1] = 1;
triangles[0].indices[2] = 2;
triangles[1].indices[0] = 1;
triangles[1].indices[1] = 3;
triangles[1].indices[2] = 2;
triangles[2].indices[0] = 4;
triangles[2].indices[1] = 5;
triangles[2].indices[2] = 6;
triangles[3].indices[0] = 5;
triangles[3].indices[1] = 7;
triangles[3].indices[2] = 6;
std::span vertex_span{ vertices.data(), vertices.size() };
sg_buffer_desc vertex_buffer_desc{
.size = vertex_span.size_bytes() * 2,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.usage = SG_USAGE_DYNAMIC,
};
vertex_buffer = sg_make_buffer(vertex_buffer_desc);
std::span vertex_span2{ vertices.data(), vertices.size() };
sg_update_buffer(vertex_buffer, sg_range{ vertex_span2.data(), vertex_span2.size_bytes() });
index_buffer = sg_make_buffer(sg_buffer_desc{
.type = SG_BUFFERTYPE_INDEXBUFFER,
.usage = SG_USAGE_IMMUTABLE,
.data = SG_RANGE(triangles),
});
in_window->state = std::move(state);
return true;
}
std::unique_ptr<mirage_render_context> mirage_create_render_context() {
return std::make_unique<mirage_windows_render_context>();
}
mirage_render_context* mirage_create_render_context() { return new windows_mirage_render_context(); }

View File

@ -3,21 +3,26 @@
#include "core/render_context.h"
class mirage_windows_render_context : public mirage_render_context {
class windows_mirage_render_context : public mirage_render_context {
public:
mirage_windows_render_context() = default;
virtual ~mirage_windows_render_context() override;
windows_mirage_render_context() = default;
virtual ~windows_mirage_render_context() override;
bool init() override;
void cleanup();
void cleanup() override;
virtual void tick(const duration_type& in_delta) override;
sg_environment get_environment() override;
virtual bool setup_surface(mirage_window* in_window, bool in_hdr) override;
bool setup_surface(mirage_window* in_window, bool in_hdr) override;
private:
ID3D11Device* device = nullptr;
ID3D11DeviceContext* device_context = nullptr;
ID3D11Device* device{};
ID3D11DeviceContext* device_context{};
IDXGIFactory* dxgi_factory = nullptr;
IDXGIFactory* dxgi_factory{};
D3D_FEATURE_LEVEL feature_level;
sg_shader shader{};
sg_pipeline pip{};
sg_buffer vertex_buffer{};
sg_buffer index_buffer{};
};

View File

@ -13,21 +13,25 @@ std::vector<mirage_window*> windows;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE:
// 标记关闭请求,但暂不销毁窗口
// 实际销毁操作应在主循环中处理
for (const auto& window : windows) {
for (const auto& window: windows) {
if (window->get_window_handle() == hwnd) {
window->close();
break;
}
}
std::erase_if(windows, [hwnd](const mirage_window* window) {
return window->get_window_handle() == hwnd;
});
std::erase_if(windows, [hwnd](const mirage_window* window) { return window->get_window_handle() == hwnd; });
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
for (const auto& window: windows) {
if (window->get_window_handle() == hwnd) {
window->on_resize(LOWORD(lParam), HIWORD(lParam));
break;
}
}
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
@ -37,21 +41,23 @@ bool mirage_window::create_window(int width, int height, const wchar_t* title) {
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = L"mirage_window";
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"mirage_window_class";
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
RegisterClass(&wc);
window_handle = (void*) CreateWindow(wc.lpszClassName,
title,
WS_OVERLAPPEDWINDOW,
0,
0,
width,
height,
nullptr,
nullptr,
wc.hInstance,
nullptr);
RECT rect = { 0, 0, width, height };
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
window_handle = (void*)CreateWindowW(
L"mirage_window_class", title,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
NULL, NULL, GetModuleHandleW(NULL), NULL
);
if (!window_handle) {
std::cerr << "Failed to create window" << std::endl;
return false;
@ -63,7 +69,12 @@ bool mirage_window::create_window(int width, int height, const wchar_t* title) {
void mirage_window::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); }
void mirage_window::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); }
void mirage_window::close() { close_request = true; DestroyWindow(WINDOW_HANDLE); }
void mirage_window::close() {
close_request = true;
DestroyWindow(WINDOW_HANDLE);
}
void mirage_window::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); }
void mirage_window::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); }
@ -80,9 +91,31 @@ void mirage_window::resize(int width, int height) {
}
Eigen::Vector2i mirage_window::get_window_size() const {
// 获取窗口大小, 包括边框和标题栏
RECT rect;
if (!GetClientRect(WINDOW_HANDLE, &rect)) { return {}; }
return { rect.right - rect.left, rect.bottom - rect.top };
if (GetWindowRect(WINDOW_HANDLE, &rect)) {
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
return Eigen::Vector2i(width, height);
}
return Eigen::Vector2i(0, 0);
}
Eigen::Vector2i mirage_window::get_window_frame_size() const {
// 获取窗口大小, 不包括边框和标题栏 (客户区大小)
RECT rect;
if (GetClientRect(WINDOW_HANDLE, &rect)) {
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
return Eigen::Vector2i(width, height);
}
return Eigen::Vector2i(0, 0);
}
Eigen::Vector2i mirage_window::get_window_position() const {
RECT rect;
if (!GetWindowRect(WINDOW_HANDLE, &rect)) { return {}; }
return { rect.left, rect.top };
}
void* mirage_window::get_window_handle() const { return window_handle; }
@ -153,3 +186,9 @@ bool mirage_window::poll_events() {
}
const std::vector<mirage_window*>& mirage_window::get_windows() { return windows; }
void mirage_window::on_resize(int width, int height) {
state->swapchain.width = width;
state->swapchain.height = height;
state->resize(Eigen::Vector2i(width, height));
}

View File

@ -0,0 +1,132 @@
#include "windows_window_state.h"
#include <iostream>
#include "misc/scope_exit.h"
bool windows_window_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, mirage_window* in_window,
bool in_hdr) {
dx_device = in_device;
dxgi_factory = in_factory;
const auto size = in_window->get_window_frame_size();
const auto window_handle = in_window->get_window_handle();
const auto format = in_hdr ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
// 创建D3D11渲染目标视图
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {
.BufferDesc = {
.Width = static_cast<UINT>(size.x()),
.Height = static_cast<UINT>(size.y()),
.RefreshRate = {
.Numerator = 240,
.Denominator = 1
},
.Format = format,
},
.SampleDesc = {
.Count = 1,
.Quality = 0
},
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferCount = 1,
.OutputWindow = (HWND) window_handle,
.Windowed = TRUE
};
// 创建交换链
HRESULT hr = dxgi_factory->CreateSwapChain(dx_device, &swap_chain_desc, &dx_swap_chain);
if (FAILED(hr)) {
std::cerr << "mirage: " << "Failed to create DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << std::dec <<
std::endl;
return false;
}
// 获取后台缓冲区
ID3D11Texture2D* back_buffer = nullptr;
hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &back_buffer);
if (FAILED(hr)) {
std::cerr << "mirage: " << "Failed to get back buffer from DXGI Swap Chain. HRESULT: 0x" << std::hex << hr <<
std::dec << std::endl;
return false;
}
ON_SCOPE_EXIT { back_buffer->Release(); };
// 创建渲染目标视图
ID3D11RenderTargetView* render_target_view = nullptr;
hr = dx_device->CreateRenderTargetView(back_buffer, nullptr, &render_target_view);
if (FAILED(hr)) {
std::cerr << "mirage: " << "Failed to create render target view. HRESULT: 0x" << std::hex << hr << std::dec <<
std::endl;
return false;
}
swapchain.d3d11.render_view = render_target_view;
swapchain.width = static_cast<int>(size.x());
swapchain.height = static_cast<int>(size.y());
swapchain.color_format = in_hdr ? SG_PIXELFORMAT_RGBA16F : SG_PIXELFORMAT_RGBA8;
swapchain.depth_format = SG_PIXELFORMAT_NONE;
swapchain.sample_count = 1;
swapchain.d3d11.resolve_view = nullptr;
swapchain.d3d11.depth_stencil_view = nullptr;
return true;
}
void windows_window_state::clear() {
dx_swap_chain->Release();
get_dx_render_target_view()->Release();
}
void windows_window_state::present() { dx_swap_chain->Present(vsync ? 1 : 0, 0); }
void windows_window_state::resize(const Eigen::Vector2i& size) {
if (size.x() == 0 || size.y() == 0) { return; }
// 保存当前格式
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
auto dx_render_target_view = get_dx_render_target_view();
if (dx_render_target_view) {
dx_render_target_view->GetDesc(&rtv_desc);
// 释放旧的渲染目标视图
dx_render_target_view->Release();
}
// 重设交换链缓冲区大小
HRESULT hr = dx_swap_chain->ResizeBuffers(
1,
// 缓冲区数量
size.x(),
// 新宽度
size.y(),
// 新高度
rtv_desc.Format,
// 保持原格式
0 // 标志
);
if (FAILED(hr)) {
// 重设失败
std::cerr << "重设交换链缓冲区失败" << std::endl;
return;
}
// 获取新的后台缓冲区
ID3D11Texture2D* back_buffer = nullptr;
hr = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &back_buffer);
if (FAILED(hr)) {
std::cerr << "获取后台缓冲区失败" << std::endl;
return;
}
ON_SCOPE_EXIT { back_buffer->Release(); };
// 创建新的渲染目标视图
hr = dx_device->CreateRenderTargetView(back_buffer,
nullptr,
(ID3D11RenderTargetView**) &swapchain.d3d11.render_view);
if (FAILED(hr)) {
std::cerr << "创建新的渲染目标视图失败" << std::endl;
return;
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#include "core/render_window.h"
#include <d3d11.h>
struct windows_window_state final : mirage_window_state {
ID3D11RenderTargetView* get_dx_render_target_view() const {
return (ID3D11RenderTargetView*)swapchain.d3d11.render_view;
}
bool init(ID3D11Device* in_device, IDXGIFactory* in_factory, mirage_window* in_window, bool in_hdr);
virtual void clear() override;
virtual void present() override;
virtual void resize(const Eigen::Vector2i& size) override;
private:
ID3D11Device* dx_device{};
IDXGIFactory* dxgi_factory{};
IDXGISwapChain* dx_swap_chain{};
};

View File

@ -1,11 +1,15 @@
#include "mirage.h"
#include <iostream>
#define SOKOL_IMPL
#include "sokol/sokol_gfx.h"
#include <iostream>
#include <thread>
#include "core/render_window.h"
#include "misc/mirage_scoped_duration_timer.h"
void mirage_log(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null,
uint32_t line_nr, const char* filename_or_null, void* user_data) {
if (log_level == 0) // painc
@ -18,21 +22,35 @@ void mirage_log(const char* tag, uint32_t log_level, uint32_t log_item_id, const
std::clog << "sg: " << message_or_null << std::endl;
}
mirage_app::mirage_app() {
}
void mirage_app::init() {
render_context = mirage_create_render_context();
render_context->init();
sg_desc desc = {
.logger = {
.func = mirage_log,
.user_data = nullptr
},
.environment = render_context->get_environment(),
};
sg_setup(desc);
duration_type duration;
{
mirage_scoped_duration_timer timer(duration);
last_time = get_current_time();
render_context = mirage_create_render_context();
render_context->init();
const sg_desc desc = {
.logger = {
.func = mirage_log,
.user_data = nullptr
},
.environment = render_context->get_environment(),
};
sg_setup(desc);
}
// 初始化用时
std::cout << "mirage: " << "Initialization took " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << "ms" << std::endl;
}
void mirage_app::run() {
while (!mirage_window::get_windows().empty()) {
duration_type delta_time = get_current_time() - last_time;
mirage_window::poll_events();
render_context->tick(delta_time);
last_time = get_current_time();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
delete render_context;
}

View File

@ -1,16 +1,17 @@
#pragma once
#include <memory>
#include "core/render_context.h"
class mirage_render_context;
class mirage_app {
public:
mirage_app();
void init();
void run();
[[nodiscard]] mirage_render_context* get_render_context() const {
return render_context;
}
private:
std::unique_ptr<mirage_render_context> render_context;
mirage_render_context* render_context{};
time_type last_time = {};
};

View File

@ -0,0 +1,15 @@
#pragma once
#include "mirage_type.h"
class mirage_scoped_duration_timer {
public:
explicit mirage_scoped_duration_timer(duration_type& out_duration) : duration(out_duration) {
start_time = get_current_time();
}
~mirage_scoped_duration_timer() {
duration = get_current_time() - start_time;
}
private:
time_type start_time;
duration_type& duration;
};

49
src/misc/mirage_type.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include <chrono>
#include <Eigen/Eigen>
using time_type = decltype(std::chrono::high_resolution_clock::now());
using duration_type = decltype(std::chrono::high_resolution_clock::now() - std::chrono::high_resolution_clock::now());
inline time_type get_current_time() {
return std::chrono::high_resolution_clock::now();
}
// struct VSInput {
// float2 position : POSITION;
// float2 uv : TEXCOORD0;
// float4 color : COLOR0;
// float4 param_a : TEXCOORD1;
// float4 param_b : TEXCOORD2;
// float4 param_c : TEXCOORD3;
// };
struct mirage_vertex_param_t{
union {
float a;
float x;
};
union {
float b;
float y;
};
union {
float c;
float z;
};
union {
float d;
float w;
};
};
struct mirage_vertex_t {
Eigen::Vector2f position;
Eigen::Vector2f uv;
Eigen::Vector4f color;
mirage_vertex_param_t param_a;
mirage_vertex_param_t param_b;
mirage_vertex_param_t param_c;
};
struct mirage_triangle_t {
uint32_t indices[3];
};

28
src/misc/scope_exit.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
template<typename FuncType>
class scope_exit_guard {
public:
scope_exit_guard(FuncType&& in_func) : func((FuncType&&)in_func) {
}
virtual ~scope_exit_guard() {
func();
}
private:
FuncType func;
};
struct scope_exit_syntax_support {
virtual ~scope_exit_syntax_support() = default;
template <typename FuncType>
scope_exit_guard<FuncType> operator+(FuncType&& InFunc)
{
return scope_exit_guard<FuncType>((FuncType&&)InFunc);
}
};
#define PRIVATE_CONCATENATE_DETAIL(x, y) x##y
#define PRIVATE_CONCATENATE(x, y) PRIVATE_CONCATENATE_DETAIL(x, y)
#define ON_SCOPE_EXIT const auto PRIVATE_CONCATENATE(scope_exit, __LINE__) = scope_exit_syntax_support() + [&]()

View File

@ -0,0 +1,85 @@
#include "mirage_util.slang"
cbuffer ParamBuffer
{
matrix transform;
};
struct PSInput {
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
float2 size : TEXCOORD1; // 矩形大小(像素单位)
float4 radius : TEXCOORD3; // 四角圆角(像素单位): [左上,右上,左下,右下]
};
[shader("vertex")]
PSInput vertex_main(VSInput input)
{
PSInput output;
// 并通过常量缓冲区或结构化缓冲区传递
output.position = mul(float4(input.position, 0.0, 1.0), transform);
output.uv = input.uv;
output.color = input.color;
// 传递尺寸和半径数据
output.size = input.param_a.xy;
output.radius = input.param_b;
return output;
}
// 改进的距离函数计算
float distance_from_rect_uv(float2 p, float2 size, float corner_radius) {
// 保护除零
corner_radius = max(corner_radius, 0.001);
// 转换圆角半径为UV空间
float2 corner_radius_uv = corner_radius / size;
// 计算内部矩形边界(圆角内切矩形)
float2 inner_rect = float2(1.0, 1.0) - corner_radius_uv;
// 计算到内部矩形的距离
float2 q = abs(p) - inner_rect;
// 转换回像素空间进行距离计算
q *= size;
// 计算SDF有符号距离场
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
{
// 将UV坐标转换为从中心点(-1,1)的归一化设备坐标
float2 p = uv_to_ndc(input.uv);
// 确定当前像素位于哪个象限,以选择正确的圆角半径
// 0=左上(x<0,y<0), 1=右上(x>0,y<0), 2=左下(x<0,y>0), 3=右下(x>0,y>0)
int idx = (p.x < 0 ? 0 : 1) + (p.y < 0 ? 0 : 2);
// 获取当前象限的圆角半径 * 2
float r = input.radius[idx];
// 计算到圆角矩形边界的有符号距离
float d = distance_from_rect_uv(p, input.size, r);
// 改进的抗锯齿算法 - 使用特定的像素宽度计算
float pixelWidth = 1.0;
float2 ddx_p = ddx(p * input.size);
float2 ddy_p = ddy(p * input.size);
// 计算基于像素梯度的边缘宽度
float edge_width = 0.5 * length(float2(length(ddx_p), length(ddy_p)));
// 应用平滑过渡 - 提高抗锯齿质量
float alpha_factor = 1.0 - smoothstep(-edge_width, edge_width, d);
// 输出最终颜色
float4 final_color = input.color;
final_color.a *= alpha_factor;
return final_color;
}

View File

@ -0,0 +1,13 @@
struct VSInput {
float2 position : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR0;
float4 param_a : TEXCOORD1;
float4 param_b : TEXCOORD2;
float4 param_c : TEXCOORD3;
};
// 将uv坐标系从[0, 0]-[1, 1] -> [-1, -1]-[1, 1]
float2 uv_to_ndc(float2 uv) {
return uv * 2 - 1;
}

27
src/shaders/test.slang Normal file
View File

@ -0,0 +1,27 @@
#include "mirage_util.slang"
struct ParamBuffer
{
matrix transform;
};
ParameterBlock<ParamBuffer> param_buffer : register(b1);
struct PSInput {
float4 position : SV_POSITION; // 裁剪空间坐标
float4 color : COLOR; // 颜色
};
[shader("vertex")]
PSInput vertex_main(VSInput input)
{
PSInput output;
output.position = mul(float4(input.position, 0, 1.0), param_buffer.transform);
output.color = input.color;
return output;
}
[shader("pixel")]
float4 pixel_main(PSInput input) : SV_TARGET
{
return input.color;
}

File diff suppressed because it is too large Load Diff

157
tools/compile_shaders.py Normal file
View File

@ -0,0 +1,157 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import subprocess
import sys
from pathlib import Path
from typing import List, Tuple, Iterator
# 支持的后端列表
BACKENDS = [
"glsl", # SG_BACKEND_GLCORE
"essl", # SG_BACKEND_GLES3
"hlsl", # SG_BACKEND_D3D11
"metal", # SG_BACKEND_METAL_IOS, SG_BACKEND_METAL_MACOS, SG_BACKEND_METAL_SIMULATOR
"wgsl", # SG_BACKEND_WGPU
]
SHADER_EXTENSIONS = {
'glsl': 'glsl',
'essl': 'glsl',
'hlsl': 'dxil',
'metal': 'metallib',
'wgsl': 'wgsl'
}
TARGET_PROFILES = {
# 'glsl': ['-profile', 'glsl_460'],
# 'essl': ['-profile', 'glsl_300es'],
# 'hlsl': ['-profile', 'sm_5_0'],
# 'metal': ['-capability', 'metallib'],
# 'wgsl': ['-profile', 'wgsl']
}
def need_recompile(input_file: Path, output_path: Path) -> bool:
"""检查是否需要重新编译"""
# 着色器输出文件名的格式为input.file.stem + '.' + backend + '.h'
# 所以需要检查是否有input.file.stem + '.*.h'文件存在
for backend in BACKENDS:
output_file = output_path / f"{input_file.stem}.{backend}.h"
if not output_file.exists():
continue
else:
input_time = input_file.stat().st_mtime
output_time = output_file.stat().st_mtime
return input_time > output_time
return True
def find_shader_files(input_dir: Path, extensions: List[str]) -> Iterator[Path]:
"""递归查找指定目录下的着色器文件"""
for file_path in Path(input_dir).rglob('*'):
if file_path.suffix in extensions:
yield file_path
def create_compiler_command(
input_file: Path,
output_file: Path,
target_type: str,
args: argparse.Namespace
) -> List[str]:
"""生成着色器编译命令"""
cmd = [args.shdc,
str(input_file),
"-o", str(output_file),
"-t", target_type,
]
if args.debug:
cmd.append('-d')
if target_type in TARGET_PROFILES:
cmd.extend(TARGET_PROFILES[target_type])
return cmd
def compile_shader(
input_file: Path,
target_types: List[Tuple[str, bool]],
args: argparse.Namespace
) -> bool:
"""编译单个着色器文件并读取二进制数据"""
try:
base = input_file.stem
output_dir = input_file.parent
success = True
for target_type, enabled in target_types:
if not enabled:
continue
if not need_recompile(input_file, output_dir):
print(f"**跳过**: {input_file} 已经是最新的")
continue
cmd = create_compiler_command(input_file, output_dir, target_type, args)
try:
# 修改这里: 明确指定编码为utf-8并添加errors参数处理无法解码的字符
subprocess.run(cmd, check=True, capture_output=True, text=True, encoding='utf-8', errors='replace')
print(f"**成功**: 编译 {input_file}")
except subprocess.CalledProcessError as e:
print(f"**错误**: 编译 {input_file} 失败")
print(e.stderr)
success = False
continue
return success
except Exception as e:
print(f"**错误**: 处理 {input_file} 时发生异常: {e}")
return False
def main():
parser = argparse.ArgumentParser(description='编译slang着色器为C++头文件')
parser.add_argument('--shdc', help='着色器编译器路径')
parser.add_argument('--shader_list', help='着色器目录列表文件路径')
parser.add_argument("--hlsl", action="store_true", help="编译HLSL着色器")
parser.add_argument("--glsl", action="store_true", help="编译GLSL着色器")
parser.add_argument("--essl", action="store_true", help="编译ESSL着色器")
parser.add_argument("--metal", action="store_true", help="编译Metal着色器")
parser.add_argument("--wgsl", action="store_true", help="编译WGSL着色器")
parser.add_argument("--debug", action="store_true", help="编译调试版本")
args = parser.parse_args()
# 确定要编译的目标后端
target_types: List[Tuple[str, bool]] = [
('glsl', args.glsl),
('essl', args.essl),
('hlsl', args.hlsl),
('metal', args.metal),
('wgsl', args.wgsl),
]
# 如果没有指定任何后端,默认启用所有后端
if not any(enabled for _, enabled in target_types):
target_types = [(backend, True) for backend, _ in target_types]
shader_list = Path(args.shader_list or "shader_paths.txt")
try:
shader_paths = shader_list.read_text(encoding="utf-8").splitlines()
except Exception as e:
print(f"**错误**: 读取着色器列表文件 {shader_list} 失败: {e}")
sys.exit(1)
all_success = True
for shader_path in shader_paths:
shader_path = shader_path.strip()
if not shader_path:
continue
for file in find_shader_files(Path(shader_path), ['.slang']):
if not compile_shader(file, target_types, args):
all_success = False
if not all_success:
sys.exit(1)
if __name__ == "__main__":
main()

BIN
tools/mirage_shdc.exe Normal file

Binary file not shown.