圆角矩形着色器
This commit is contained in:
parent
3479b9443b
commit
4727a47616
10
.gitignore
vendored
10
.gitignore
vendored
@ -3,3 +3,13 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/scripts/shader_paths.txt
|
/scripts/shader_paths.txt
|
||||||
/cache/shader_loader.h
|
/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
|
||||||
|
@ -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)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# MSVC编译器设置C++标准
|
# MSVC编译器设置C++标准
|
||||||
add_compile_options(/std:c++latest)
|
add_compile_options(/std:c++latest)
|
||||||
# 设置utf-8编码
|
# 设置utf-8编码
|
||||||
add_compile_options(/utf-8)
|
add_compile_options(/utf-8)
|
||||||
endif ()
|
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_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE)
|
||||||
set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG 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_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(MIRAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
# 设置全局着色器输出目录
|
|
||||||
set(SHADER_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resource/shaders" CACHE PATH "着色器编译输出路径")
|
|
||||||
|
|
||||||
include(cmake/retrieve_files.cmake)
|
include(cmake/retrieve_files.cmake)
|
||||||
include(cmake/detect_os.cmake)
|
include(cmake/detect_os.cmake)
|
||||||
include(cmake/compile_shaders.cmake)
|
|
||||||
include(cmake/configure_glfw_native.cmake)
|
|
||||||
include(cmake/config_macos.cmake)
|
include(cmake/config_macos.cmake)
|
||||||
|
include(cmake/compile_shaders.cmake)
|
||||||
|
|
||||||
# 如果是Debug模式, 添加宏定义
|
# 如果是Debug模式, 添加宏定义
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
@ -1,56 +1,75 @@
|
|||||||
# 查找编译器和Python
|
include(CMakeParseArguments)
|
||||||
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)
|
|
||||||
|
|
||||||
message(STATUS "Python解释器: ${PYTHON_EXECUTABLE}")
|
# 查找Python解释器
|
||||||
message(STATUS "SLANG编译器: ${SLANG_COMPILER}")
|
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_COMPILE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/tools/compile_shaders.py")
|
||||||
set(SHADER_HEADER_FILE ${SHADER_HEADER_DIR}/shader_loader.h)
|
|
||||||
# 创建文件
|
|
||||||
file(WRITE ${SHADER_HEADER_FILE} "")
|
|
||||||
|
|
||||||
# 删除文件
|
# 在cache目录下创建着色器目录.txt
|
||||||
|
set(SHADER_PATH_FILE ${CMAKE_CACHEFILE_DIR}/shader_paths.txt)
|
||||||
file(REMOVE ${SHADER_PATH_FILE})
|
file(REMOVE ${SHADER_PATH_FILE})
|
||||||
|
file(WRITE ${SHADER_PATH_FILE} "")
|
||||||
|
|
||||||
function(shader_compile_target INPUT_DIR)
|
# 添加着色器目录的函数
|
||||||
# 将路径写到scripts/shader_paths.txt中
|
function(add_mirage_shader_directory path)
|
||||||
file(WRITE ${SHADER_PATH_FILE} ${INPUT_DIR})
|
# 将路径写入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()
|
endfunction()
|
||||||
|
|
||||||
set(SLANGC_ARGS "")
|
set(SHADER_COMPILE_ARGS "")
|
||||||
if (LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGLES3 OR LLGL_BUILD_RENDERER_WEBGL)
|
set(SHADER_SHDC "")
|
||||||
list(APPEND SLANGC_ARGS "--opengl")
|
if (WIN32)
|
||||||
endif ()
|
list(APPEND SHADER_COMPILE_ARGS "--hlsl")
|
||||||
if (LLGL_BUILD_RENDERER_VULKAN)
|
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc.exe)
|
||||||
list(APPEND SLANGC_ARGS "--vulkan")
|
elseif (APPLE)
|
||||||
endif ()
|
list(APPEND SHADER_COMPILE_ARGS "--metal")
|
||||||
if (LLGL_BUILD_RENDERER_DIRECT3D11)
|
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc)
|
||||||
list(APPEND SLANGC_ARGS "--d3d11")
|
else()
|
||||||
endif ()
|
list(APPEND SHADER_COMPILE_ARGS "--glsl")
|
||||||
if (LLGL_BUILD_RENDERER_DIRECT3D12)
|
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mirage_shdc)
|
||||||
list(APPEND SLANGC_ARGS "--d3d12")
|
endif()
|
||||||
endif ()
|
|
||||||
if (LLGL_BUILD_RENDERER_METAL)
|
|
||||||
list(APPEND SLANGC_ARGS "--metal")
|
|
||||||
endif ()
|
|
||||||
# 如果是Debug模式, 添加--debug选项
|
# 如果是Debug模式, 添加--debug选项
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
list(APPEND SLANGC_ARGS "--debug")
|
list(APPEND SHADER_COMPILE_ARGS "--debug")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
message(STATUS "着色器编译输出路径: ${SHADER_OUTPUT_DIR}")
|
# 添加编译目标, 调用tools/compile_shaders.py
|
||||||
|
add_custom_target(compile_shaders ALL
|
||||||
# 添加自定义命令, 用于编译着色器, 调用scripts/compile_shaders.py
|
COMMAND ${Python3_EXECUTABLE} ${SHADER_COMPILE_SCRIPT}
|
||||||
add_custom_target(compile_shaders
|
--shdc ${SHADER_SHDC}
|
||||||
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}
|
--shader_list ${SHADER_PATH_FILE}
|
||||||
COMMENT "编译着色器"
|
${SHADER_COMPILE_ARGS}
|
||||||
VERBATIM
|
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()
|
||||||
|
@ -5,4 +5,6 @@ set(SRC_FILES "")
|
|||||||
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES)
|
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES)
|
||||||
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE mirage_core)
|
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()
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
mirage_app app;
|
mirage_app app;
|
||||||
|
app.init();
|
||||||
|
|
||||||
mirage_window window;
|
mirage_window window;
|
||||||
window.create_window(800, 600, L"Hello, World!");
|
window.create_window(800, 600, L"Hello, World!");
|
||||||
window.show();
|
window.show();
|
||||||
|
app.get_render_context()->setup_surface(&window, false);
|
||||||
|
|
||||||
while (!mirage_window::get_windows().empty()) {
|
app.run();
|
||||||
mirage_window::poll_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.init();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
@ -2,17 +2,6 @@ cmake_minimum_required(VERSION 3.15)
|
|||||||
|
|
||||||
project(mirage_core LANGUAGES C CXX)
|
project(mirage_core LANGUAGES C CXX)
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
if (MSVC)
|
|
||||||
# MSVC编译器设置C++标准
|
|
||||||
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(Freetype REQUIRED)
|
||||||
find_package(Eigen3 REQUIRED)
|
find_package(Eigen3 REQUIRED)
|
||||||
@ -23,14 +12,14 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES)
|
|||||||
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
|
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype Eigen3::Eigen)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype Eigen3::Eigen)
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
configure_glfw_native(${PROJECT_NAME})
|
|
||||||
add_os_definitions(${PROJECT_NAME})
|
add_os_definitions(${PROJECT_NAME})
|
||||||
|
|
||||||
|
# 添加编译shader的自定义命令
|
||||||
|
add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/shaders)
|
||||||
|
add_shader_dependencies(${PROJECT_NAME})
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_D3D11=1)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC -DSOKOL_D3D11)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE d3d11 dxgi)
|
|
||||||
elseif (APPLE)
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_METAL=1)
|
|
||||||
elseif (UNIX)
|
elseif (UNIX)
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC SOKOL_GLCORE=1)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC -DSOKOL_GLCORE33)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "sokol/sokol_gfx.h"
|
#include "sokol/sokol_gfx.h"
|
||||||
#include <memory>
|
|
||||||
|
#include "misc/mirage_type.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class mirage_window;
|
class mirage_window;
|
||||||
|
|
||||||
|
|
||||||
class mirage_render_context {
|
class mirage_render_context {
|
||||||
public:
|
public:
|
||||||
virtual ~mirage_render_context() = default;
|
virtual ~mirage_render_context() = default;
|
||||||
|
|
||||||
virtual bool init() { return false; }
|
virtual bool init() { return false; }
|
||||||
|
virtual void cleanup() { }
|
||||||
|
virtual void tick(const duration_type& in_delta) = 0;
|
||||||
virtual sg_environment get_environment() = 0;
|
virtual sg_environment get_environment() = 0;
|
||||||
virtual bool setup_surface(mirage_window* in_window, bool in_hdr) { return false; }
|
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();
|
||||||
|
@ -1,3 +1,29 @@
|
|||||||
#include "render_window.h"
|
#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());
|
||||||
|
}
|
||||||
|
@ -2,6 +2,23 @@
|
|||||||
#include "windows/windows_render_context.h"
|
#include "windows/windows_render_context.h"
|
||||||
#include <Eigen/Eigen>
|
#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 {
|
class mirage_window {
|
||||||
public:
|
public:
|
||||||
bool create_window(int width, int height, const wchar_t* title);
|
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()); }
|
void move(const Eigen::Vector2i& pos) { move(pos.x(), pos.y()); }
|
||||||
|
|
||||||
[[nodiscard]] Eigen::Vector2i get_window_size() const;
|
[[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;
|
[[nodiscard]] void* get_window_handle() const;
|
||||||
|
|
||||||
// style functions
|
// style functions
|
||||||
@ -34,6 +57,10 @@ public:
|
|||||||
[[nodiscard]] bool close_requested() const { return close_request; }
|
[[nodiscard]] bool close_requested() const { return close_request; }
|
||||||
static bool poll_events();
|
static bool poll_events();
|
||||||
static const std::vector<mirage_window*>& get_windows();
|
static const std::vector<mirage_window*>& get_windows();
|
||||||
|
|
||||||
|
void on_resize(int width, int height);
|
||||||
|
|
||||||
|
std::unique_ptr<mirage_window_state> state;
|
||||||
private:
|
private:
|
||||||
void* window_handle{};
|
void* window_handle{};
|
||||||
bool close_request = false;
|
bool close_request = false;
|
||||||
|
@ -5,141 +5,216 @@
|
|||||||
#include "windows_render_context.h"
|
#include "windows_render_context.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
#include "core/render_window.h"
|
#include "core/render_window.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
mirage_windows_render_context::~mirage_windows_render_context() {
|
#include "windows_window_state.h"
|
||||||
cleanup();
|
#include "misc/scope_exit.h"
|
||||||
}
|
#include "shaders/test.hlsl.h"
|
||||||
|
#include "shaders/mirage_rounded_rect.hlsl.h"
|
||||||
|
|
||||||
bool mirage_windows_render_context::init() {
|
windows_mirage_render_context::~windows_mirage_render_context() { cleanup(); }
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
// 设置设备创建标志
|
bool windows_mirage_render_context::init() {
|
||||||
UINT device_flags = 0;
|
try {
|
||||||
#if DEBUG
|
// 定义支持的特性级别(从高到低排序)
|
||||||
device_flags |= D3D11_CREATE_DEVICE_DEBUG; // 在Debug模式下启用调试层
|
D3D_FEATURE_LEVEL feature_levels[] = {
|
||||||
#endif
|
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[] = {
|
UINT device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED; // BGRA支持和单线程模式
|
||||||
D3D_DRIVER_TYPE_HARDWARE, // 首选硬件加速
|
#if DEBUG
|
||||||
D3D_DRIVER_TYPE_WARP, // 其次是WARP软件渲染器
|
device_flags |= D3D11_CREATE_DEVICE_DEBUG; // 在Debug模式下启用调试层
|
||||||
D3D_DRIVER_TYPE_REFERENCE // 最后是参考软件渲染器
|
#endif
|
||||||
};
|
|
||||||
|
|
||||||
// 尝试按优先级创建设备
|
// 定义要尝试的驱动类型数组
|
||||||
HRESULT hr = E_FAIL;
|
constexpr D3D_DRIVER_TYPE driver_types[] = {
|
||||||
D3D_DRIVER_TYPE used_driver_type = D3D_DRIVER_TYPE_UNKNOWN;
|
D3D_DRIVER_TYPE_HARDWARE,
|
||||||
|
// 首选硬件加速
|
||||||
|
D3D_DRIVER_TYPE_WARP,
|
||||||
|
// 其次是WARP软件渲染器
|
||||||
|
D3D_DRIVER_TYPE_REFERENCE // 最后是参考软件渲染器
|
||||||
|
};
|
||||||
|
|
||||||
for (const auto& driver_type : driver_types) {
|
// 尝试按优先级创建设备
|
||||||
hr = D3D11CreateDevice(
|
HRESULT hr = E_FAIL;
|
||||||
nullptr, // 使用默认适配器
|
D3D_DRIVER_TYPE used_driver_type = D3D_DRIVER_TYPE_UNKNOWN;
|
||||||
driver_type, // 驱动类型
|
// 使用 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2(推荐)
|
||||||
nullptr, // 软件栅格化模块句柄(仅用于软件设备)
|
if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
|
||||||
device_flags, // 设备创建标志
|
std::cout << "mirage: " << "DPI awareness set to per-monitor aware v2" << std::endl;
|
||||||
feature_levels, // 特性级别数组
|
}
|
||||||
ARRAYSIZE(feature_levels), // 特性级别数量
|
|
||||||
D3D11_SDK_VERSION, // SDK版本
|
|
||||||
&device, // 输出设备
|
|
||||||
&feature_level, // 输出获取的特性级别
|
|
||||||
&device_context // 输出设备上下文
|
|
||||||
);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
for (const auto& driver_type: driver_types) {
|
||||||
used_driver_type = driver_type;
|
hr = D3D11CreateDevice(
|
||||||
break;
|
nullptr,
|
||||||
}
|
// 使用默认适配器
|
||||||
}
|
driver_type,
|
||||||
|
// 驱动类型
|
||||||
|
nullptr,
|
||||||
|
// 软件栅格化模块句柄(仅用于软件设备)
|
||||||
|
device_flags,
|
||||||
|
// 设备创建标志
|
||||||
|
feature_levels,
|
||||||
|
// 特性级别数组
|
||||||
|
ARRAYSIZE(feature_levels),
|
||||||
|
// 特性级别数量
|
||||||
|
D3D11_SDK_VERSION,
|
||||||
|
// SDK版本
|
||||||
|
&device,
|
||||||
|
// 输出设备
|
||||||
|
&feature_level,
|
||||||
|
// 输出获取的特性级别
|
||||||
|
&device_context // 输出设备上下文
|
||||||
|
);
|
||||||
|
|
||||||
// 检查是否成功创建设备
|
if (SUCCEEDED(hr)) {
|
||||||
if (FAILED(hr)) {
|
used_driver_type = driver_type;
|
||||||
std::cerr << "Failed to create D3D11 device with any driver type. HRESULT: 0x"
|
break;
|
||||||
<< std::hex << hr << std::dec << std::endl;
|
}
|
||||||
cleanup();
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输出使用设备
|
// 检查是否成功创建设备
|
||||||
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;
|
IDXGIAdapter* dxgi_adapter = nullptr;
|
||||||
IDXGIDevice* dxgi_device = nullptr;
|
IDXGIDevice* dxgi_device = nullptr;
|
||||||
hr = device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
|
hr = device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
hr = dxgi_device->GetAdapter(&dxgi_adapter);
|
hr = dxgi_device->GetAdapter(&dxgi_adapter);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
hr = dxgi_adapter->GetDesc(&adapter_desc);
|
hr = dxgi_adapter->GetDesc(&adapter_desc);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) { std::wcerr << L"mirage: " << L"Failed to get DXGI Adapter description" << std::endl; }
|
||||||
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; }
|
||||||
hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&dxgi_factory));
|
dxgi_adapter->Release();
|
||||||
if (FAILED(hr)) {
|
}
|
||||||
dxgi_factory = nullptr;
|
dxgi_device->Release();
|
||||||
}
|
}
|
||||||
dxgi_adapter->Release();
|
|
||||||
}
|
|
||||||
dxgi_device->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dxgi_factory) {
|
if (!dxgi_factory) {
|
||||||
std::cerr << "Failed to get DXGI Factory" << std::endl;
|
std::cerr << "Failed to get DXGI Factory" << std::endl;
|
||||||
cleanup();
|
cleanup();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输出初始化成功信息
|
// 输出初始化成功信息
|
||||||
std::cout << "D3D11 device created successfully" << std::endl;
|
std::cout << "mirage: " << "D3D11 device created successfully" << std::endl;
|
||||||
std::wcout << L"Using adapter: " << adapter_desc.Description << std::endl;
|
std::wcout << L"mirage: " << L"Using adapter: " << adapter_desc.Description << std::endl;
|
||||||
|
|
||||||
// 输出驱动类型信息
|
// 输出驱动类型信息
|
||||||
auto driver_type_str = "Unknown";
|
auto driver_type_str = "Unknown";
|
||||||
switch (used_driver_type) {
|
switch (used_driver_type) {
|
||||||
case D3D_DRIVER_TYPE_HARDWARE: driver_type_str = "Hardware"; break;
|
case D3D_DRIVER_TYPE_HARDWARE:
|
||||||
case D3D_DRIVER_TYPE_WARP: driver_type_str = "WARP"; break;
|
driver_type_str = "Hardware";
|
||||||
case D3D_DRIVER_TYPE_REFERENCE: driver_type_str = "Reference"; break;
|
break;
|
||||||
default: ;
|
case D3D_DRIVER_TYPE_WARP:
|
||||||
}
|
driver_type_str = "WARP";
|
||||||
std::cout << "Using driver type: " << driver_type_str << std::endl;
|
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";
|
auto feature_level_str = "Unknown";
|
||||||
switch (feature_level) {
|
switch (feature_level) {
|
||||||
case D3D_FEATURE_LEVEL_11_1: feature_level_str = "11.1"; break;
|
case D3D_FEATURE_LEVEL_11_1:
|
||||||
case D3D_FEATURE_LEVEL_11_0: feature_level_str = "11.0"; break;
|
feature_level_str = "11.1";
|
||||||
case D3D_FEATURE_LEVEL_10_1: feature_level_str = "10.1"; break;
|
break;
|
||||||
case D3D_FEATURE_LEVEL_10_0: feature_level_str = "10.0"; break;
|
case D3D_FEATURE_LEVEL_11_0:
|
||||||
default: ;
|
feature_level_str = "11.0";
|
||||||
}
|
break;
|
||||||
std::cout << "Using feature level: " << feature_level_str << std::endl;
|
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;
|
return true;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
catch (const std::exception& e) {
|
std::cerr << "mirage: " << "Exception during D3D11 initialization: " << e.what() << std::endl;
|
||||||
std::cerr << "Exception during D3D11 initialization: " << e.what() << std::endl;
|
cleanup();
|
||||||
cleanup();
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 资源清理函数
|
// 资源清理函数
|
||||||
void mirage_windows_render_context::cleanup() {
|
void windows_mirage_render_context::cleanup() {
|
||||||
// 安全释放COM接口
|
mirage_render_context::cleanup();
|
||||||
if (device_context) { device_context->Release(); device_context = nullptr; }
|
// 安全释放COM接口
|
||||||
if (device) { device->Release(); device = nullptr; }
|
sg_shutdown();
|
||||||
if (dxgi_factory) { dxgi_factory->Release(); dxgi_factory = nullptr; }
|
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 {
|
return {
|
||||||
.d3d11 = {
|
.d3d11 = {
|
||||||
.device = device,
|
.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) {
|
bool windows_mirage_render_context::setup_surface(mirage_window* in_window, bool in_hdr) {
|
||||||
const auto& window_size = in_window->get_window_size();
|
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渲染目标视图
|
sg_pipeline_desc pipeline_desc = get_mirage_rounded_rect_pipeline_desc(shader, state->swapchain);
|
||||||
IDXGISwapChain* swap_chain = nullptr;
|
pip = sg_make_pipeline(pipeline_desc);
|
||||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {
|
|
||||||
.BufferDesc = {
|
mirage_triangle_t triangles[4];
|
||||||
.Width = static_cast<UINT>(window_size.x()),
|
|
||||||
.Height = static_cast<UINT>(window_size.y()),
|
// 创建顶点缓冲区(一个矩形)
|
||||||
.RefreshRate = {
|
std::vector<mirage_vertex_t> vertices = {
|
||||||
.Numerator = 60,
|
// x, y, r, g, b, a
|
||||||
.Denominator = 1
|
{ { 0.f, 0.f }, { 0.f, 0.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
|
||||||
},
|
// 左上 - 黄色
|
||||||
.Format = format,
|
{ { 100.f, 0.f }, { 1.f, 0.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
|
||||||
},
|
// 右上 - 蓝色
|
||||||
.SampleDesc = {
|
{ { 0.f, 100.f }, { 0.f, 1.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
|
||||||
.Count = 1,
|
// 左下 - 红色
|
||||||
.Quality = 0
|
{ { 100.f, 100.f }, { 1.f, 1.f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
|
||||||
},
|
// 右下 - 绿色
|
||||||
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
{ { 200.f, 200.f }, { 0.f, 0.f }, { 0.0f, 1.0f, 1.0f, 1.0f } },
|
||||||
.BufferCount = 1,
|
// 左上 - 黄色
|
||||||
.OutputWindow = nullptr,
|
{ { 300.f, 200.f }, { 1.f, 0.f }, { 1.0f, 0.0f, 1.0f, 1.0f } },
|
||||||
.Windowed = TRUE
|
// 右上 - 蓝色
|
||||||
|
{ { 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<mirage_render_context> mirage_create_render_context() {
|
mirage_render_context* mirage_create_render_context() { return new windows_mirage_render_context(); }
|
||||||
return std::make_unique<mirage_windows_render_context>();
|
|
||||||
}
|
|
||||||
|
@ -3,21 +3,26 @@
|
|||||||
|
|
||||||
#include "core/render_context.h"
|
#include "core/render_context.h"
|
||||||
|
|
||||||
class mirage_windows_render_context : public mirage_render_context {
|
class windows_mirage_render_context : public mirage_render_context {
|
||||||
public:
|
public:
|
||||||
mirage_windows_render_context() = default;
|
windows_mirage_render_context() = default;
|
||||||
virtual ~mirage_windows_render_context() override;
|
virtual ~windows_mirage_render_context() override;
|
||||||
|
|
||||||
bool init() override;
|
bool init() override;
|
||||||
|
void cleanup() override;
|
||||||
void cleanup();
|
virtual void tick(const duration_type& in_delta) override;
|
||||||
|
|
||||||
sg_environment get_environment() 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:
|
private:
|
||||||
ID3D11Device* device = nullptr;
|
ID3D11Device* device{};
|
||||||
ID3D11DeviceContext* device_context = nullptr;
|
ID3D11DeviceContext* device_context{};
|
||||||
|
|
||||||
IDXGIFactory* dxgi_factory = nullptr;
|
IDXGIFactory* dxgi_factory{};
|
||||||
D3D_FEATURE_LEVEL feature_level;
|
D3D_FEATURE_LEVEL feature_level;
|
||||||
|
|
||||||
|
sg_shader shader{};
|
||||||
|
sg_pipeline pip{};
|
||||||
|
sg_buffer vertex_buffer{};
|
||||||
|
sg_buffer index_buffer{};
|
||||||
};
|
};
|
||||||
|
@ -13,21 +13,25 @@ std::vector<mirage_window*> windows;
|
|||||||
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
// 标记关闭请求,但暂不销毁窗口
|
for (const auto& window: windows) {
|
||||||
// 实际销毁操作应在主循环中处理
|
|
||||||
for (const auto& window : windows) {
|
|
||||||
if (window->get_window_handle() == hwnd) {
|
if (window->get_window_handle() == hwnd) {
|
||||||
window->close();
|
window->close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::erase_if(windows, [hwnd](const mirage_window* window) {
|
std::erase_if(windows, [hwnd](const mirage_window* window) { return window->get_window_handle() == hwnd; });
|
||||||
return window->get_window_handle() == hwnd;
|
|
||||||
});
|
|
||||||
return 0;
|
return 0;
|
||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
return 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:
|
default:
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
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 = {};
|
WNDCLASS wc = {};
|
||||||
wc.lpfnWndProc = WindowProc;
|
wc.lpfnWndProc = WindowProc;
|
||||||
wc.hInstance = GetModuleHandle(nullptr);
|
wc.hInstance = GetModuleHandle(nullptr);
|
||||||
wc.lpszClassName = L"mirage_window";
|
wc.lpszClassName = L"mirage_window_class";
|
||||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
||||||
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||||
|
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
|
||||||
RegisterClass(&wc);
|
RegisterClass(&wc);
|
||||||
|
|
||||||
window_handle = (void*) CreateWindow(wc.lpszClassName,
|
RECT rect = { 0, 0, width, height };
|
||||||
title,
|
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
||||||
WS_OVERLAPPEDWINDOW,
|
|
||||||
0,
|
window_handle = (void*)CreateWindowW(
|
||||||
0,
|
L"mirage_window_class", title,
|
||||||
width,
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||||
height,
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
nullptr,
|
rect.right - rect.left, rect.bottom - rect.top,
|
||||||
nullptr,
|
NULL, NULL, GetModuleHandleW(NULL), NULL
|
||||||
wc.hInstance,
|
);
|
||||||
nullptr);
|
|
||||||
if (!window_handle) {
|
if (!window_handle) {
|
||||||
std::cerr << "Failed to create window" << std::endl;
|
std::cerr << "Failed to create window" << std::endl;
|
||||||
return false;
|
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::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); }
|
||||||
void mirage_window::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); }
|
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::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); }
|
||||||
void mirage_window::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); }
|
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 {
|
Eigen::Vector2i mirage_window::get_window_size() const {
|
||||||
|
// 获取窗口大小, 包括边框和标题栏
|
||||||
RECT rect;
|
RECT rect;
|
||||||
if (!GetClientRect(WINDOW_HANDLE, &rect)) { return {}; }
|
if (GetWindowRect(WINDOW_HANDLE, &rect)) {
|
||||||
return { rect.right - rect.left, rect.bottom - rect.top };
|
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; }
|
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; }
|
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));
|
||||||
|
}
|
||||||
|
132
src/core/windows/windows_window_state.cpp
Normal file
132
src/core/windows/windows_window_state.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
21
src/core/windows/windows_window_state.h
Normal file
21
src/core/windows/windows_window_state.h
Normal 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{};
|
||||||
|
};
|
@ -1,11 +1,15 @@
|
|||||||
#include "mirage.h"
|
#include "mirage.h"
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#define SOKOL_IMPL
|
#define SOKOL_IMPL
|
||||||
#include "sokol/sokol_gfx.h"
|
#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,
|
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) {
|
uint32_t line_nr, const char* filename_or_null, void* user_data) {
|
||||||
if (log_level == 0) // painc
|
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;
|
std::clog << "sg: " << message_or_null << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
mirage_app::mirage_app() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void mirage_app::init() {
|
void mirage_app::init() {
|
||||||
render_context = mirage_create_render_context();
|
duration_type duration;
|
||||||
render_context->init();
|
{
|
||||||
sg_desc desc = {
|
mirage_scoped_duration_timer timer(duration);
|
||||||
.logger = {
|
|
||||||
.func = mirage_log,
|
last_time = get_current_time();
|
||||||
.user_data = nullptr
|
render_context = mirage_create_render_context();
|
||||||
},
|
render_context->init();
|
||||||
.environment = render_context->get_environment(),
|
const sg_desc desc = {
|
||||||
};
|
.logger = {
|
||||||
sg_setup(desc);
|
.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() {
|
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;
|
||||||
}
|
}
|
||||||
|
11
src/mirage.h
11
src/mirage.h
@ -1,16 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <memory>
|
|
||||||
#include "core/render_context.h"
|
#include "core/render_context.h"
|
||||||
|
|
||||||
class mirage_render_context;
|
class mirage_render_context;
|
||||||
|
|
||||||
class mirage_app {
|
class mirage_app {
|
||||||
public:
|
public:
|
||||||
mirage_app();
|
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
[[nodiscard]] mirage_render_context* get_render_context() const {
|
||||||
|
return render_context;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<mirage_render_context> render_context;
|
mirage_render_context* render_context{};
|
||||||
|
time_type last_time = {};
|
||||||
};
|
};
|
||||||
|
15
src/misc/mirage_scoped_duration_timer.h
Normal file
15
src/misc/mirage_scoped_duration_timer.h
Normal 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
49
src/misc/mirage_type.h
Normal 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
28
src/misc/scope_exit.h
Normal 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() + [&]()
|
||||||
|
|
85
src/shaders/mirage_rounded_rect.slang
Normal file
85
src/shaders/mirage_rounded_rect.slang
Normal 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;
|
||||||
|
}
|
13
src/shaders/mirage_util.slang
Normal file
13
src/shaders/mirage_util.slang
Normal 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
27
src/shaders/test.slang
Normal 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
157
tools/compile_shaders.py
Normal 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
BIN
tools/mirage_shdc.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user