├── .gitignore ├── CMakeLists.txt ├── ReadMe.md ├── cmake ├── CopyFiles.cmake ├── FindSDL2.cmake └── FindVulkan.cmake ├── resources ├── role.png └── texture.jpg ├── sandbox ├── CMakeLists.txt └── sandbox.cpp ├── shader ├── shader.frag └── shader.vert ├── snapshot └── snapshot.png ├── src ├── buffer.cpp ├── command_manager.cpp ├── context.cpp ├── descriptor_manager.cpp ├── math.cpp ├── render_process.cpp ├── renderer.cpp ├── shader.cpp ├── swapchain.cpp ├── texture.cpp ├── tool.cpp └── toy2d.cpp ├── toy2d ├── buffer.hpp ├── command_manager.hpp ├── context.hpp ├── descriptor_manager.hpp ├── math.hpp ├── render_process.hpp ├── renderer.hpp ├── shader.hpp ├── stb_image.h ├── swapchain.hpp ├── texture.hpp ├── tool.hpp └── toy2d.hpp └── welcome.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .cache 3 | cmake-build 4 | *.json 5 | *.spv 6 | compile_flags.txt 7 | 8 | # Ignore those directories 9 | build/ 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 3 | 4 | project(Toy2D 5 | LANGUAGES CXX 6 | DESCRIPTION "a toy 2d renderer made in vulkan") 7 | 8 | include(cmake/FindVulkan.cmake) 9 | include(cmake/FindSDL2.cmake) 10 | include(cmake/CopyFiles.cmake) 11 | 12 | find_program(GLSLC_PROGRAM glslc REQUIRED) 13 | 14 | message(STATUS "run glslc to compile shaders ...") 15 | execute_process(COMMAND ${GLSLC_PROGRAM} ${CMAKE_SOURCE_DIR}/shader/shader.vert -o ${CMAKE_SOURCE_DIR}/vert.spv) 16 | execute_process(COMMAND ${GLSLC_PROGRAM} ${CMAKE_SOURCE_DIR}/shader/shader.frag -o ${CMAKE_SOURCE_DIR}/frag.spv) 17 | message(STATUS "compile shader OK") 18 | 19 | aux_source_directory(src SRC) 20 | 21 | add_library(toy2d STATIC ${SRC}) 22 | target_include_directories(toy2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 23 | target_link_libraries(toy2d PUBLIC Vulkan::Vulkan) 24 | target_compile_features(toy2d PUBLIC cxx_std_17) 25 | 26 | add_subdirectory(sandbox) 27 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # toy2D 2 | 3 | Toy2d是一个模仿`SDL2_Renderer`功能的2D玩具渲染器,是我在学习Vulkan过程中编写的。 4 | 5 | 我把我的学习过程录制成视频放在[B站](https://space.bilibili.com/256768793/channel/collectiondetail?sid=404887)了,有兴趣的可以看看。每个视频对应一个分支,可前往不同分支获得不同阶段的代码。 6 | 7 | 主分支是最终代码。 8 | 9 | ## 编译 10 | 11 | 工程使用CMake。需要预先安装好VulkanSDK 12 | 13 | Linux和MacOSX下安装好SDL2,然后运行 14 | 15 | ```cmake 16 | cmake -S . -B cmake-build 17 | cmake --build cmake-build 18 | ``` 19 | 20 | Windows下我只使用VS编译了(其他平台未测试)。下载[编译好的SDL2文件](https://github.com/libsdl-org/SDL/releases/tag/release-2.26.5), 然后再CMake的时候指定SDL2路径: 21 | 22 | ```text 23 | SDL2_ROOT = /SDL2-2.0.22-VC 24 | ``` 25 | 26 | 然后编译 27 | 28 | ```cmake 29 | cmake -S . -B cmake-build 30 | cmake --build cmake-build 31 | ``` 32 | 33 | 产生`sandbox`可执行文件。请在工程根目录下运行(便于找到资源文件)。 34 | -------------------------------------------------------------------------------- /cmake/CopyFiles.cmake: -------------------------------------------------------------------------------- 1 | macro(CopyDLL target_name) 2 | if (WIN32) 3 | add_custom_command( 4 | TARGET ${target_name} POST_BUILD 5 | COMMAND ${CMAKE_COMMAND} -E copy ${SDL2_ROOT}/lib/x64/SDL2.dll $) 6 | # If you have selected SDL2 component when installed Vulkan SDK, the command as follows will work 7 | # add_custom_command( 8 | # TARGET ${target_name} POST_BUILD 9 | # COMMAND ${CMAKE_COMMAND} -E copy ${SDL2_BIN_DIR}/SDL2.dll $) 10 | endif() 11 | endmacro(CopyDLL) 12 | 13 | macro(CopyShader target_name) 14 | add_custom_command( 15 | TARGET ${target_name} POST_BUILD 16 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/vert.spv $) 17 | add_custom_command( 18 | TARGET ${target_name} POST_BUILD 19 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/frag.spv $) 20 | endmacro(CopyShader) 21 | 22 | macro(CopyTexture target_name) 23 | add_custom_command( 24 | TARGET ${target_name} POST_BUILD 25 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/resources $/resources) 26 | endmacro(CopyTexture) 27 | -------------------------------------------------------------------------------- /cmake/FindSDL2.cmake: -------------------------------------------------------------------------------- 1 | if (NOT TARGET SDL2) 2 | if (WIN32) # Windows, use clang or MSVC 3 | set(SDL2_ROOT "" CACHE PATH "SDL2 root directory") 4 | set(SDL2_INCLUDE_DIR "${SDL2_ROOT}/include") 5 | set(SDL2_LIB_DIR "${SDL2_ROOT}/lib/x64") 6 | add_library(SDL2::SDL2 SHARED IMPORTED GLOBAL) 7 | set_target_properties( 8 | SDL2::SDL2 9 | PROPERTIES 10 | IMPORTED_LOCATION "${SDL2_LIB_DIR}/SDL2.dll" 11 | IMPORTED_IMPLIB "${SDL2_LIB_DIR}/SDL2.lib" 12 | INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} 13 | ) 14 | add_library(SDL2::SDL2main SHARED IMPORTED GLOBAL) 15 | set_target_properties( 16 | SDL2::SDL2main 17 | PROPERTIES 18 | IMPORTED_LOCATION "${SDL2_LIB_DIR}/SDL2.dll" 19 | IMPORTED_IMPLIB "${SDL2_LIB_DIR}/SDL2main.lib" 20 | INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} 21 | ) 22 | add_library(SDL2 INTERFACE IMPORTED GLOBAL) 23 | target_link_libraries(SDL2 INTERFACE SDL2::SDL2 SDL2::SDL2main) 24 | else() # Linux, MacOSX 25 | find_package(SDL2 QUIET) 26 | if (SDL2_FOUND) 27 | add_library(SDL2 ALIAS SDL2::SDL2) 28 | else() 29 | find_package(PkgConfig REQUIRED) 30 | pkg_check_modules(SDL2 sdl2 REQUIRED IMPORTED_TARGET) 31 | add_library(SDL2 ALIAS PkgConfig::SDL2) 32 | endif() 33 | endif() 34 | endif() 35 | 36 | # If you have selected SDL2 component when installed Vulkan SDK 37 | # The following settings will work 38 | # if (NOT TARGET SDL2) 39 | # if (WIN32) # Windows, use clang or MSVC 40 | # set(SDL2_ROOT "" CACHE PATH "SDL2 root directory") 41 | # set(SDL2_INCLUDE_DIR "${Vulkan_INCLUDE_DIRS}") 42 | # set(SDL2_LIB_DIR "$ENV{VULKAN_SDK}/Lib") 43 | # set(SDL2_BIN_DIR "$ENV{VULKAN_SDK}/Bin") 44 | # add_library(SDL2::SDL2 SHARED IMPORTED GLOBAL) 45 | # set_target_properties( 46 | # SDL2::SDL2 47 | # PROPERTIES 48 | # IMPORTED_LOCATION "${SDL2_BIN_DIR}/SDL2.dll" 49 | # IMPORTED_IMPLIB "${SDL2_LIB_DIR}/SDL2.lib" 50 | # INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} 51 | # ) 52 | # add_library(SDL2::SDL2main SHARED IMPORTED GLOBAL) 53 | # set_target_properties( 54 | # SDL2::SDL2main 55 | # PROPERTIES 56 | # IMPORTED_LOCATION "${SDL2_BIN_DIR}/SDL2.dll" 57 | # IMPORTED_IMPLIB "${SDL2_LIB_DIR}/SDL2main.lib" 58 | # INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} 59 | # ) 60 | # add_library(SDL2 INTERFACE IMPORTED GLOBAL) 61 | # target_link_libraries(SDL2 INTERFACE SDL2::SDL2 SDL2::SDL2main) 62 | # else() # Linux, MacOSX 63 | # find_package(SDL2 QUIET) 64 | # if (SDL2_FOUND) 65 | # add_library(SDL2 ALIAS SDL2::SDL2) 66 | # else() 67 | # find_package(PkgConfig REQUIRED) 68 | # pkg_check_modules(SDL2 sdl2 REQUIRED IMPORTED_TARGET) 69 | # add_library(SDL2 ALIAS PkgConfig::SDL2) 70 | # endif() 71 | # endif() 72 | # endif() 73 | -------------------------------------------------------------------------------- /cmake/FindVulkan.cmake: -------------------------------------------------------------------------------- 1 | find_package(Vulkan REQUIRED) 2 | # set(VULKAN_DIR "" CACHE PATH "Vulkan root dir, example: C:/VulkanSDK/1.3.216.0") 3 | # 4 | # if (NOT TARGET Vulkan::Vulkan) 5 | # if (WIN32) # Windows, use clang or MSVC 6 | # set(VULKAN_INCLUDE_DIR "${VULKAN_DIR}/Include") 7 | # set(VULKAN_LIB_LOCATION "${VULKAN_DIR}/Lib/vulkan-1.lib") 8 | # add_library(Vulkan::Vulkan STATIC IMPORTED GLOBAL) 9 | # set_target_properties( 10 | # Vulkan::Vulkan 11 | # PROPERTIES 12 | # IMPORTED_LOCATION ${VULKAN_LIB_LOCATION} 13 | # INTERFACE_INCLUDE_DIRECTORIES ${VULKAN_INCLUDE_DIR} 14 | # ) 15 | # else() # Unix or MacOSX 16 | # find_package(Vulkan REQUIRED) 17 | # endif() 18 | # endif() 19 | -------------------------------------------------------------------------------- /resources/role.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualGMQ/toy2d/d9d8145e0599e47a243159205cd6454385ac66ff/resources/role.png -------------------------------------------------------------------------------- /resources/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualGMQ/toy2d/d9d8145e0599e47a243159205cd6454385ac66ff/resources/texture.jpg -------------------------------------------------------------------------------- /sandbox/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(sandbox) 2 | aux_source_directory(./ SANDBOX_SRC) 3 | target_sources(sandbox PRIVATE ${SANDBOX_SRC}) 4 | target_link_libraries(sandbox PUBLIC toy2d SDL2) 5 | CopyDLL(sandbox) 6 | CopyShader(sandbox) 7 | CopyTexture(sandbox) 8 | -------------------------------------------------------------------------------- /sandbox/sandbox.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/toy2d.hpp" 2 | #include "SDL.h" 3 | #include "SDL_vulkan.h" 4 | #include 5 | 6 | // If you have selected SDL2 component when installed Vulkan SDK 7 | // The following codes will work 8 | // #include 9 | // #include 10 | // #include 11 | 12 | constexpr uint32_t WindowWidth = 1024; 13 | constexpr uint32_t WindowHeight = 720; 14 | 15 | int main(int argc, char** argv) { 16 | SDL_Init(SDL_INIT_EVERYTHING); 17 | 18 | SDL_Window* window = SDL_CreateWindow("sandbox", 19 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 20 | WindowWidth, WindowHeight, 21 | SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE|SDL_WINDOW_VULKAN); 22 | if (!window) { 23 | SDL_Log("create window failed"); 24 | exit(2); 25 | } 26 | 27 | unsigned int count; 28 | SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr); 29 | std::vector extensions(count); 30 | SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data()); 31 | 32 | #ifdef __APPLE__ 33 | extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); 34 | #endif 35 | 36 | toy2d::Init(extensions, 37 | [&](VkInstance instance){ 38 | VkSurfaceKHR surface; 39 | SDL_Vulkan_CreateSurface(window, instance, &surface); 40 | return surface; 41 | }, 1024, 720); 42 | auto renderer = toy2d::GetRenderer(); 43 | 44 | bool shouldClose = false; 45 | SDL_Event event; 46 | 47 | float x = 100, y = 100; 48 | 49 | toy2d::Texture* texture1 = toy2d::LoadTexture("resources/role.png"); 50 | toy2d::Texture* texture2 = toy2d::LoadTexture("resources/texture.jpg"); 51 | 52 | while (!shouldClose) { 53 | while (SDL_PollEvent(&event)) { 54 | if (event.type == SDL_QUIT) { 55 | shouldClose = true; 56 | } 57 | if (event.type == SDL_KEYDOWN) { 58 | if (event.key.keysym.sym == SDLK_a) { 59 | x -= 10; 60 | } 61 | if (event.key.keysym.sym == SDLK_d) { 62 | x += 10; 63 | } 64 | if (event.key.keysym.sym == SDLK_w) { 65 | y -= 10; 66 | } 67 | if (event.key.keysym.sym == SDLK_s) { 68 | y += 10; 69 | } 70 | } 71 | if (event.type == SDL_WINDOWEVENT) { 72 | if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { 73 | toy2d::ResizeSwapchainImage(event.window.data1, event.window.data2); 74 | } 75 | } 76 | } 77 | 78 | renderer->StartRender(); 79 | renderer->SetDrawColor(toy2d::Color{1, 0, 0}); 80 | renderer->DrawTexture(toy2d::Rect{toy2d::Vec{x, y}, toy2d::Size{200, 300}}, *texture1); 81 | renderer->SetDrawColor(toy2d::Color{0, 1, 0}); 82 | renderer->DrawTexture(toy2d::Rect{toy2d::Vec{500, 100}, toy2d::Size{200, 300}}, *texture2); 83 | renderer->SetDrawColor(toy2d::Color{0, 0, 1}); 84 | renderer->DrawLine(toy2d::Vec{0, 0}, toy2d::Vec{WindowWidth, WindowHeight}); 85 | renderer->EndRender(); 86 | } 87 | 88 | toy2d::DestroyTexture(texture1); 89 | toy2d::DestroyTexture(texture2); 90 | 91 | toy2d::Quit(); 92 | 93 | SDL_DestroyWindow(window); 94 | SDL_Quit(); 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /shader/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) out vec4 outColor; 4 | layout(location = 0) in vec2 Texcoord; 5 | 6 | layout(set = 1, binding = 0) uniform sampler2D Sampler; 7 | 8 | layout(push_constant) uniform PushConstant { 9 | layout(offset = 64) vec3 color; 10 | } pc; 11 | 12 | void main() { 13 | outColor = vec4(pc.color, 1.0) * texture(Sampler, Texcoord); 14 | } 15 | -------------------------------------------------------------------------------- /shader/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 inPosition; 4 | layout(location = 1) in vec2 inTexcoord; 5 | 6 | layout(location = 0) out vec2 outTexcoord; 7 | 8 | layout(set = 0, binding = 0) uniform UniformBuffer { 9 | mat4 project; 10 | mat4 view; 11 | } ubo; 12 | 13 | layout(push_constant) uniform PushConstant { 14 | mat4 model; 15 | } pc; 16 | 17 | void main() { 18 | gl_Position = ubo.project * ubo.view * pc.model * vec4(inPosition, 0.0, 1.0); 19 | outTexcoord = inTexcoord; 20 | } 21 | -------------------------------------------------------------------------------- /snapshot/snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualGMQ/toy2d/d9d8145e0599e47a243159205cd6454385ac66ff/snapshot/snapshot.png -------------------------------------------------------------------------------- /src/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/buffer.hpp" 2 | 3 | namespace toy2d { 4 | 5 | Buffer::Buffer(vk::BufferUsageFlags usage, size_t size, vk::MemoryPropertyFlags memProperty) { 6 | auto& device = Context::Instance().device; 7 | 8 | this->size = size; 9 | vk::BufferCreateInfo createInfo; 10 | createInfo.setUsage(usage) 11 | .setSize(size) 12 | .setSharingMode(vk::SharingMode::eExclusive); 13 | 14 | buffer = device.createBuffer(createInfo); 15 | 16 | auto requirements = device.getBufferMemoryRequirements(buffer); 17 | requireSize = requirements.size; 18 | auto index = QueryBufferMemTypeIndex(requirements.memoryTypeBits, memProperty); 19 | vk::MemoryAllocateInfo allocInfo; 20 | allocInfo.setMemoryTypeIndex(index) 21 | .setAllocationSize(requirements.size); 22 | memory = device.allocateMemory(allocInfo); 23 | 24 | device.bindBufferMemory(buffer, memory, 0); 25 | 26 | if (memProperty & vk::MemoryPropertyFlagBits::eHostVisible) { 27 | map = device.mapMemory(memory, 0, size); 28 | } else { 29 | map = nullptr; 30 | } 31 | } 32 | 33 | Buffer::~Buffer() { 34 | auto& device = Context::Instance().device; 35 | if (map) { 36 | device.unmapMemory(memory); 37 | } 38 | device.freeMemory(memory); 39 | device.destroyBuffer(buffer); 40 | } 41 | 42 | std::uint32_t QueryBufferMemTypeIndex(std::uint32_t type, vk::MemoryPropertyFlags flag) { 43 | auto property = Context::Instance().phyDevice.getMemoryProperties(); 44 | 45 | for (std::uint32_t i = 0; i < property.memoryTypeCount; i++) { 46 | if ((1 << i) & type && 47 | property.memoryTypes[i].propertyFlags & flag) { 48 | return i; 49 | } 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/command_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/command_manager.hpp" 2 | #include "toy2d/context.hpp" 3 | 4 | namespace toy2d { 5 | 6 | CommandManager::CommandManager() { 7 | pool_ = createCommandPool(); 8 | } 9 | 10 | CommandManager::~CommandManager() { 11 | auto& ctx = Context::Instance(); 12 | ctx.device.destroyCommandPool(pool_); 13 | } 14 | 15 | void CommandManager::ResetCmds() { 16 | Context::Instance().device.resetCommandPool(pool_); 17 | } 18 | 19 | vk::CommandPool CommandManager::createCommandPool() { 20 | auto& ctx = Context::Instance(); 21 | 22 | vk::CommandPoolCreateInfo createInfo; 23 | 24 | createInfo.setQueueFamilyIndex(ctx.queueInfo.graphicsIndex.value()) 25 | .setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer); 26 | 27 | return ctx.device.createCommandPool(createInfo); 28 | } 29 | 30 | std::vector CommandManager::CreateCommandBuffers(std::uint32_t count) { 31 | auto& ctx = Context::Instance(); 32 | 33 | vk::CommandBufferAllocateInfo allocInfo; 34 | allocInfo.setCommandPool(pool_) 35 | .setCommandBufferCount(1) 36 | .setLevel(vk::CommandBufferLevel::ePrimary); 37 | 38 | return ctx.device.allocateCommandBuffers(allocInfo); 39 | } 40 | 41 | vk::CommandBuffer CommandManager::CreateOneCommandBuffer() { 42 | return CreateCommandBuffers(1)[0]; 43 | } 44 | 45 | void CommandManager::FreeCmd(const vk::CommandBuffer& cmdBuf) { 46 | Context::Instance().device.freeCommandBuffers(pool_, cmdBuf); 47 | } 48 | 49 | void CommandManager::ExecuteCmd(vk::Queue queue, RecordCmdFunc func) { 50 | auto cmdBuf = CreateOneCommandBuffer(); 51 | 52 | vk::CommandBufferBeginInfo beginInfo; 53 | beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); 54 | cmdBuf.begin(beginInfo); 55 | if (func) func(cmdBuf); 56 | cmdBuf.end(); 57 | 58 | vk::SubmitInfo submitInfo; 59 | submitInfo.setCommandBuffers(cmdBuf); 60 | queue.submit(submitInfo); 61 | queue.waitIdle(); 62 | Context::Instance().device.waitIdle(); 63 | FreeCmd(cmdBuf); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/context.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/context.hpp" 2 | 3 | namespace toy2d { 4 | 5 | Context* Context::instance_ = nullptr; 6 | 7 | void Context::Init(std::vector& extensions, GetSurfaceCallback cb) { 8 | instance_ = new Context(extensions, cb); 9 | } 10 | 11 | void Context::Quit() { 12 | delete instance_; 13 | } 14 | 15 | Context& Context::Instance() { 16 | return *instance_; 17 | } 18 | 19 | Context::Context(std::vector& extensions, GetSurfaceCallback cb) { 20 | getSurfaceCb_ = cb; 21 | 22 | instance = createInstance(extensions); 23 | if (!instance) { 24 | std::cout << "instance create failed" << std::endl; 25 | exit(1); 26 | } 27 | 28 | phyDevice = pickupPhysicalDevice(); 29 | if (!phyDevice) { 30 | std::cout << "pickup physical device failed" << std::endl; 31 | exit(1); 32 | } 33 | 34 | getSurface(); 35 | 36 | device = createDevice(surface_); 37 | if (!device) { 38 | std::cout << "create device failed" << std::endl; 39 | exit(1); 40 | } 41 | 42 | graphicsQueue = device.getQueue(queueInfo.graphicsIndex.value(), 0); 43 | presentQueue = device.getQueue(queueInfo.presentIndex.value(), 0); 44 | } 45 | 46 | void Context::getSurface() { 47 | surface_ = getSurfaceCb_(instance); 48 | if (!surface_) { 49 | std::cout << "create surface failed" << std::endl; 50 | exit(1); 51 | } 52 | } 53 | 54 | vk::Instance Context::createInstance(std::vector& extensions) { 55 | vk::InstanceCreateInfo info; 56 | vk::ApplicationInfo appInfo; 57 | appInfo.setApiVersion(VK_API_VERSION_1_3); 58 | info.setPApplicationInfo(&appInfo) 59 | .setPEnabledExtensionNames(extensions); 60 | 61 | std::vector layers = {"VK_LAYER_KHRONOS_validation"}; 62 | info.setPEnabledLayerNames(layers); 63 | 64 | #ifdef __APPLE__ 65 | info.setFlags(vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR); 66 | #endif 67 | 68 | return vk::createInstance(info); 69 | } 70 | 71 | vk::PhysicalDevice Context::pickupPhysicalDevice() { 72 | auto devices = instance.enumeratePhysicalDevices(); 73 | if (devices.size() == 0) { 74 | std::cout << "you don't have suitable device to support vulkan" << std::endl; 75 | exit(1); 76 | } 77 | return devices[0]; 78 | } 79 | 80 | vk::Device Context::createDevice(vk::SurfaceKHR surface) { 81 | vk::DeviceCreateInfo deviceCreateInfo; 82 | queryQueueInfo(surface); 83 | std::array extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; 84 | deviceCreateInfo.setPEnabledExtensionNames(extensions); 85 | 86 | std::vector queueInfos; 87 | float priority = 1; 88 | if (queueInfo.graphicsIndex.value() == queueInfo.presentIndex.value()) { 89 | vk::DeviceQueueCreateInfo queueCreateInfo; 90 | queueCreateInfo.setPQueuePriorities(&priority); 91 | queueCreateInfo.setQueueCount(1); 92 | queueCreateInfo.setQueueFamilyIndex(queueInfo.graphicsIndex.value()); 93 | queueInfos.push_back(queueCreateInfo); 94 | } else { 95 | vk::DeviceQueueCreateInfo queueCreateInfo; 96 | queueCreateInfo.setPQueuePriorities(&priority); 97 | queueCreateInfo.setQueueCount(1); 98 | queueCreateInfo.setQueueFamilyIndex(queueInfo.graphicsIndex.value()); 99 | queueInfos.push_back(queueCreateInfo); 100 | 101 | queueCreateInfo.setQueueFamilyIndex(queueInfo.presentIndex.value()); 102 | queueInfos.push_back(queueCreateInfo); 103 | } 104 | deviceCreateInfo.setQueueCreateInfos(queueInfos); 105 | 106 | return phyDevice.createDevice(deviceCreateInfo); 107 | } 108 | 109 | void Context::queryQueueInfo(vk::SurfaceKHR surface) { 110 | auto queueProps = phyDevice.getQueueFamilyProperties(); 111 | for (int i = 0; i < queueProps.size(); i++) { 112 | if (queueProps[i].queueFlags & vk::QueueFlagBits::eGraphics) { 113 | queueInfo.graphicsIndex = i; 114 | } 115 | 116 | if (phyDevice.getSurfaceSupportKHR(i, surface)) { 117 | queueInfo.presentIndex = i; 118 | } 119 | 120 | if (queueInfo.graphicsIndex.has_value() && 121 | queueInfo.presentIndex.has_value()) { 122 | break; 123 | } 124 | } 125 | } 126 | 127 | void Context::initSwapchain(int windowWidth, int windowHeight) { 128 | swapchain = std::make_unique(surface_, windowWidth, windowHeight); 129 | } 130 | 131 | void Context::initRenderProcess() { 132 | renderProcess = std::make_unique(); 133 | } 134 | 135 | void Context::initGraphicsPipeline() { 136 | renderProcess->CreateGraphicsPipeline(*shader); 137 | } 138 | 139 | void Context::initCommandPool() { 140 | commandManager = std::make_unique(); 141 | } 142 | 143 | void Context::initShaderModules() { 144 | auto vertexSource = ReadWholeFile("./vert.spv"); 145 | auto fragSource = ReadWholeFile("./frag.spv"); 146 | shader = std::make_unique(vertexSource, fragSource); 147 | } 148 | 149 | void Context::initSampler() { 150 | vk::SamplerCreateInfo createInfo; 151 | createInfo.setMagFilter(vk::Filter::eLinear) 152 | .setMinFilter(vk::Filter::eLinear) 153 | .setAddressModeU(vk::SamplerAddressMode::eRepeat) 154 | .setAddressModeV(vk::SamplerAddressMode::eRepeat) 155 | .setAddressModeW(vk::SamplerAddressMode::eRepeat) 156 | .setAnisotropyEnable(false) 157 | .setBorderColor(vk::BorderColor::eIntOpaqueBlack) 158 | .setUnnormalizedCoordinates(false) 159 | .setCompareEnable(false) 160 | .setMipmapMode(vk::SamplerMipmapMode::eLinear); 161 | sampler = Context::Instance().device.createSampler(createInfo); 162 | } 163 | 164 | Context::~Context() { 165 | shader.reset(); 166 | device.destroySampler(sampler); 167 | commandManager.reset(); 168 | renderProcess.reset(); 169 | swapchain.reset(); 170 | device.destroy(); 171 | instance.destroy(); 172 | } 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/descriptor_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/descriptor_manager.hpp" 2 | #include "toy2d/context.hpp" 3 | 4 | namespace toy2d { 5 | 6 | std::unique_ptr DescriptorSetManager::instance_ = nullptr; 7 | 8 | DescriptorSetManager::DescriptorSetManager(uint32_t maxFlight): maxFlight_(maxFlight) { 9 | vk::DescriptorPoolSize size; 10 | size.setType(vk::DescriptorType::eUniformBuffer) 11 | .setDescriptorCount(2 * maxFlight); 12 | vk::DescriptorPoolCreateInfo createInfo; 13 | createInfo.setMaxSets(maxFlight) 14 | .setPoolSizes(size); 15 | auto pool = Context::Instance().device.createDescriptorPool(createInfo); 16 | bufferSetPool_.pool_ = pool; 17 | bufferSetPool_.remainNum_ = maxFlight; 18 | } 19 | 20 | DescriptorSetManager::~DescriptorSetManager() { 21 | auto& device = Context::Instance().device; 22 | 23 | device.destroyDescriptorPool(bufferSetPool_.pool_); 24 | for (auto pool : fulledImageSetPool_) { 25 | device.destroyDescriptorPool(pool.pool_); 26 | } 27 | for (auto pool : avalibleImageSetPool_) { 28 | device.destroyDescriptorPool(pool.pool_); 29 | } 30 | } 31 | 32 | void DescriptorSetManager::addImageSetPool() { 33 | constexpr uint32_t MaxSetNum = 10; 34 | 35 | vk::DescriptorPoolSize size; 36 | size.setType(vk::DescriptorType::eCombinedImageSampler) 37 | .setDescriptorCount(MaxSetNum); 38 | vk::DescriptorPoolCreateInfo createInfo; 39 | createInfo.setMaxSets(MaxSetNum) 40 | .setPoolSizes(size) 41 | .setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); 42 | auto pool = Context::Instance().device.createDescriptorPool(createInfo); 43 | avalibleImageSetPool_.push_back({pool, MaxSetNum}); 44 | } 45 | 46 | std::vector DescriptorSetManager::AllocBufferSets(uint32_t num) { 47 | std::vector layouts(maxFlight_, Context::Instance().shader->GetDescriptorSetLayouts()[0]); 48 | vk::DescriptorSetAllocateInfo allocInfo; 49 | allocInfo.setDescriptorPool(bufferSetPool_.pool_) 50 | .setDescriptorSetCount(num) 51 | .setSetLayouts(layouts); 52 | auto sets = Context::Instance().device.allocateDescriptorSets(allocInfo); 53 | 54 | std::vector result(num); 55 | 56 | for (int i = 0; i < num; i++) { 57 | result[i].set = sets[i]; 58 | result[i].pool = bufferSetPool_.pool_; 59 | } 60 | 61 | return result; 62 | } 63 | 64 | DescriptorSetManager::SetInfo DescriptorSetManager::AllocImageSet() { 65 | std::vector layouts{ Context::Instance().shader->GetDescriptorSetLayouts()[1] }; 66 | vk::DescriptorSetAllocateInfo allocInfo; 67 | auto& poolInfo = getAvaliableImagePoolInfo(); 68 | allocInfo.setDescriptorPool(poolInfo.pool_) 69 | .setDescriptorSetCount(1) 70 | .setSetLayouts(layouts); 71 | auto sets = Context::Instance().device.allocateDescriptorSets(allocInfo); 72 | 73 | SetInfo result; 74 | result.pool = poolInfo.pool_; 75 | result.set = sets[0]; 76 | 77 | poolInfo.remainNum_ = std::max(static_cast(poolInfo.remainNum_) - sets.size(), 0); 78 | if (poolInfo.remainNum_ == 0) { 79 | fulledImageSetPool_.push_back(poolInfo); 80 | avalibleImageSetPool_.pop_back(); 81 | } 82 | 83 | return result; 84 | } 85 | 86 | void DescriptorSetManager::FreeImageSet(const SetInfo& info) { 87 | auto it = std::find_if(fulledImageSetPool_.begin(), fulledImageSetPool_.end(), 88 | [&](const PoolInfo& poolInfo) { 89 | return poolInfo.pool_ == info.pool; 90 | }); 91 | if (it != fulledImageSetPool_.end()) { 92 | it->remainNum_ ++; 93 | avalibleImageSetPool_.push_back(*it); 94 | fulledImageSetPool_.erase(it); 95 | return; 96 | } 97 | 98 | it = std::find_if(avalibleImageSetPool_.begin(), avalibleImageSetPool_.end(), 99 | [&](const PoolInfo& poolInfo) { 100 | return poolInfo.pool_ == info.pool; 101 | }); 102 | if (it != avalibleImageSetPool_.end()) { 103 | it->remainNum_ ++; 104 | return; 105 | } 106 | } 107 | 108 | DescriptorSetManager::PoolInfo& DescriptorSetManager::getAvaliableImagePoolInfo() { 109 | if (avalibleImageSetPool_.empty()) { 110 | addImageSetPool(); 111 | return avalibleImageSetPool_.back(); 112 | } 113 | return avalibleImageSetPool_.back(); 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/math.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/math.hpp" 2 | 3 | namespace toy2d { 4 | 5 | std::vector Vec::GetAttributeDescription() { 6 | std::vector descriptions(2); 7 | descriptions[0].setBinding(0) 8 | .setFormat(vk::Format::eR32G32Sfloat) 9 | .setLocation(0) 10 | .setOffset(0); 11 | descriptions[1].setBinding(0) 12 | .setFormat(vk::Format::eR32G32Sfloat) 13 | .setLocation(1) 14 | .setOffset(offsetof(Vertex, texcoord)); 15 | return descriptions; 16 | } 17 | 18 | std::vector Vec::GetBindingDescription() { 19 | std::vector descriptions(1); 20 | descriptions[0].setBinding(0) 21 | .setStride(sizeof(Vertex)) 22 | .setInputRate(vk::VertexInputRate::eVertex); 23 | return descriptions; 24 | } 25 | 26 | Mat4 Mat4::Create(const std::initializer_list& initList) { 27 | Mat4 mat; 28 | int counter = 0; 29 | for (auto& value : initList) { 30 | int x = counter % 4; 31 | int y = counter / 4; 32 | mat.Set(y, x, value); 33 | counter ++; 34 | } 35 | return mat; 36 | } 37 | 38 | Mat4::Mat4() { 39 | memset(data_, 0, sizeof(float) * 4 * 4); 40 | for (int i = 0; i < 4; i++) { 41 | Set(i, i, 1); 42 | } 43 | } 44 | 45 | Mat4 Mat4::CreateOnes() { 46 | Mat4 mat; 47 | memset(mat.data_, 1, sizeof(float) * 4 * 4); 48 | return mat; 49 | } 50 | 51 | Mat4 Mat4::CreateIdentity() { 52 | return Mat4{}; 53 | } 54 | 55 | Mat4 Mat4::CreateOrtho(int left, int right, int top, int bottom, int near, int far) { 56 | Mat4 mat = CreateIdentity(); 57 | 58 | mat.Set(0, 0, 2.0 / (right - left)); 59 | mat.Set(1, 1, 2.0 / (top - bottom)); 60 | mat.Set(2, 2, 2.0 / (near - far)); 61 | mat.Set(3, 0, (left + right) / (left - right)); 62 | mat.Set(3, 1, (top + bottom) / (bottom - top)); 63 | mat.Set(3, 2, (near + far) / (far - near)); 64 | 65 | return mat; 66 | } 67 | 68 | Mat4 Mat4::CreateTranslate(const Vec& pos) { 69 | Mat4 mat = CreateIdentity(); 70 | 71 | mat.Set(3, 0, pos.x); 72 | mat.Set(3, 1, pos.y); 73 | 74 | return mat; 75 | } 76 | 77 | Mat4 Mat4::CreateScale(const Vec& scale) { 78 | Mat4 mat = CreateIdentity(); 79 | 80 | mat.Set(0, 0, scale.x); 81 | mat.Set(1, 1, scale.y); 82 | 83 | return mat; 84 | } 85 | 86 | Mat4 Mat4::Mul(const Mat4& m) const { 87 | Mat4 mat; 88 | for (int i = 0; i < 4; i++) { 89 | for (int j = 0; j < 4; j++) { 90 | int sum = 0; 91 | for (int k = 0; k < 4; k++) { 92 | sum += Get(k, i) * m.Get(j, k); 93 | } 94 | mat.Set(j, i, sum); 95 | } 96 | } 97 | return mat; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/render_process.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/render_process.hpp" 2 | #include "toy2d/context.hpp" 3 | #include "toy2d/swapchain.hpp" 4 | #include "toy2d/math.hpp" 5 | 6 | namespace toy2d { 7 | 8 | RenderProcess::RenderProcess() { 9 | layout = createLayout(); 10 | CreateRenderPass(); 11 | graphicsPipelineWithTriangleTopology = nullptr; 12 | } 13 | 14 | RenderProcess::~RenderProcess() { 15 | auto& ctx = Context::Instance(); 16 | auto& device = ctx.device; 17 | device.destroyPipelineCache(pipelineCache_); 18 | device.destroyRenderPass(renderPass); 19 | device.destroyPipelineLayout(layout); 20 | device.destroyPipeline(graphicsPipelineWithTriangleTopology); 21 | device.destroyPipeline(graphicsPipelineWithLineTopology); 22 | } 23 | 24 | void RenderProcess::CreateGraphicsPipeline(const Shader& shader) { 25 | graphicsPipelineWithTriangleTopology = createGraphicsPipeline(shader, vk::PrimitiveTopology::eTriangleList); 26 | graphicsPipelineWithLineTopology = createGraphicsPipeline(shader, vk::PrimitiveTopology::eLineList); 27 | } 28 | 29 | void RenderProcess::CreateRenderPass() { 30 | renderPass = createRenderPass(); 31 | } 32 | 33 | vk::PipelineLayout RenderProcess::createLayout() { 34 | vk::PipelineLayoutCreateInfo createInfo; 35 | auto range = Context::Instance().shader->GetPushConstantRange(); 36 | createInfo.setSetLayouts(Context::Instance().shader->GetDescriptorSetLayouts()) 37 | .setPushConstantRanges(range); 38 | 39 | return Context::Instance().device.createPipelineLayout(createInfo); 40 | } 41 | 42 | vk::Pipeline RenderProcess::createGraphicsPipeline(const Shader& shader, vk::PrimitiveTopology topology) { 43 | auto& ctx = Context::Instance(); 44 | 45 | vk::GraphicsPipelineCreateInfo createInfo; 46 | 47 | // 0. shader prepare 48 | std::array stageCreateInfos; 49 | stageCreateInfos[0].setModule(shader.GetVertexModule()) 50 | .setPName("main") 51 | .setStage(vk::ShaderStageFlagBits::eVertex); 52 | stageCreateInfos[1].setModule(shader.GetFragModule()) 53 | .setPName("main") 54 | .setStage(vk::ShaderStageFlagBits::eFragment); 55 | 56 | // 1. vertex input 57 | vk::PipelineVertexInputStateCreateInfo vertexInputCreateInfo; 58 | auto attributeDesc = Vec::GetAttributeDescription(); 59 | auto bindingDesc = Vec::GetBindingDescription(); 60 | vertexInputCreateInfo.setVertexAttributeDescriptions(attributeDesc) 61 | .setVertexBindingDescriptions(bindingDesc); 62 | 63 | // 2. vertex assembly 64 | vk::PipelineInputAssemblyStateCreateInfo inputAsmCreateInfo; 65 | inputAsmCreateInfo.setPrimitiveRestartEnable(false) 66 | .setTopology(topology); 67 | 68 | // 3. viewport and scissor 69 | vk::PipelineViewportStateCreateInfo viewportInfo; 70 | vk::Viewport viewport(0, 0, ctx.swapchain->GetExtent().width, ctx.swapchain->GetExtent().height, 0, 1); 71 | vk::Rect2D scissor(vk::Rect2D({0, 0}, ctx.swapchain->GetExtent())); 72 | viewportInfo.setViewports(viewport) 73 | .setScissors(scissor); 74 | 75 | // 4. rasteraizer 76 | vk::PipelineRasterizationStateCreateInfo rasterInfo; 77 | rasterInfo.setCullMode(vk::CullModeFlagBits::eFront) 78 | .setFrontFace(vk::FrontFace::eCounterClockwise) 79 | .setDepthClampEnable(false) 80 | .setLineWidth(1) 81 | .setPolygonMode(vk::PolygonMode::eFill) 82 | .setRasterizerDiscardEnable(false); 83 | 84 | // 5. multisampler 85 | vk::PipelineMultisampleStateCreateInfo multisampleInfo; 86 | multisampleInfo.setSampleShadingEnable(false) 87 | .setRasterizationSamples(vk::SampleCountFlagBits::e1); 88 | 89 | // 6. depth and stencil buffer 90 | // We currently don't need depth and stencil buffer 91 | 92 | // 7. blending 93 | /* 94 | * newRGB = (srcFactor * srcRGB) (dstFactor * dstRGB) 95 | * newA = (srcFactor * srcA) (dstFactor * dstA) 96 | * 97 | * newRGB = 1 * srcRGB + (1 - srcA) * dstRGB 98 | * newA = srcA === 1 * srcA + 0 * dstA 99 | */ 100 | vk::PipelineColorBlendAttachmentState blendAttachmentState; 101 | blendAttachmentState.setBlendEnable(true) 102 | .setColorWriteMask(vk::ColorComponentFlagBits::eA| 103 | vk::ColorComponentFlagBits::eB| 104 | vk::ColorComponentFlagBits::eG| 105 | vk::ColorComponentFlagBits::eR) 106 | .setSrcColorBlendFactor(vk::BlendFactor::eOne) 107 | .setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha) 108 | .setColorBlendOp(vk::BlendOp::eAdd) 109 | .setSrcAlphaBlendFactor(vk::BlendFactor::eOne) 110 | .setDstAlphaBlendFactor(vk::BlendFactor::eZero) 111 | .setAlphaBlendOp(vk::BlendOp::eAdd); 112 | 113 | vk::PipelineColorBlendStateCreateInfo blendInfo; 114 | blendInfo.setAttachments(blendAttachmentState) 115 | .setLogicOpEnable(false); 116 | 117 | // create graphics pipeline 118 | createInfo.setStages(stageCreateInfos) 119 | .setLayout(layout) 120 | .setPVertexInputState(&vertexInputCreateInfo) 121 | .setPInputAssemblyState(&inputAsmCreateInfo) 122 | .setPViewportState(&viewportInfo) 123 | .setPRasterizationState(&rasterInfo) 124 | .setPMultisampleState(&multisampleInfo) 125 | .setPColorBlendState(&blendInfo) 126 | .setRenderPass(renderPass); 127 | 128 | auto result = ctx.device.createGraphicsPipeline(pipelineCache_, createInfo); 129 | if (result.result != vk::Result::eSuccess) { 130 | std::cout << "create graphics pipeline failed: " << result.result << std::endl; 131 | } 132 | 133 | return result.value; 134 | } 135 | 136 | vk::RenderPass RenderProcess::createRenderPass() { 137 | auto& ctx = Context::Instance(); 138 | 139 | vk::RenderPassCreateInfo createInfo; 140 | 141 | vk::SubpassDependency dependency; 142 | dependency.setSrcSubpass(VK_SUBPASS_EXTERNAL) 143 | .setDstSubpass(0) 144 | .setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) 145 | .setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) 146 | .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite); 147 | 148 | vk::AttachmentDescription attachDescription; 149 | attachDescription.setFormat(ctx.swapchain->GetFormat().format) 150 | .setSamples(vk::SampleCountFlagBits::e1) 151 | .setLoadOp(vk::AttachmentLoadOp::eClear) 152 | .setStoreOp(vk::AttachmentStoreOp::eStore) 153 | .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare) 154 | .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) 155 | .setInitialLayout(vk::ImageLayout::eUndefined) 156 | .setFinalLayout(vk::ImageLayout::ePresentSrcKHR); 157 | vk::AttachmentReference reference; 158 | reference.setAttachment(0) 159 | .setLayout(vk::ImageLayout::eColorAttachmentOptimal); 160 | 161 | vk::SubpassDescription subpassDesc; 162 | subpassDesc.setColorAttachments(reference) 163 | .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics); 164 | 165 | subpassDesc.setColorAttachments(reference); 166 | createInfo.setAttachments(attachDescription) 167 | .setDependencies(dependency) 168 | .setSubpasses(subpassDesc); 169 | 170 | return Context::Instance().device.createRenderPass(createInfo); 171 | } 172 | 173 | vk::PipelineCache RenderProcess::createPipelineCache() { 174 | vk::PipelineCacheCreateInfo createInfo; 175 | createInfo.setInitialDataSize(0); 176 | 177 | return Context::Instance().device.createPipelineCache(createInfo); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/renderer.hpp" 2 | #include "toy2d/math.hpp" 3 | #include "toy2d/context.hpp" 4 | 5 | namespace toy2d { 6 | 7 | Renderer::Renderer(int maxFlightCount): maxFlightCount_(maxFlightCount), curFrame_(0) { 8 | createFences(); 9 | createSemaphores(); 10 | createCmdBuffers(); 11 | createBuffers(); 12 | createUniformBuffers(maxFlightCount); 13 | descriptorSets_ = DescriptorSetManager::Instance().AllocBufferSets(maxFlightCount); 14 | updateDescriptorSets(); 15 | initMats(); 16 | createWhiteTexture(); 17 | 18 | SetDrawColor(Color{1, 1, 1}); 19 | } 20 | 21 | Renderer::~Renderer() { 22 | auto& device = Context::Instance().device; 23 | device.destroySampler(sampler); 24 | rectVerticesBuffer_.reset(); 25 | rectIndicesBuffer_.reset(); 26 | uniformBuffers_.clear(); 27 | for (auto& sem : imageAvaliableSems_) { 28 | device.destroySemaphore(sem); 29 | } 30 | for (auto& sem : renderFinishSems_) { 31 | device.destroySemaphore(sem); 32 | } 33 | for (auto& fence : fences_) { 34 | device.destroyFence(fence); 35 | } 36 | } 37 | 38 | void Renderer::StartRender() { 39 | auto& ctx = Context::Instance(); 40 | auto& device = ctx.device; 41 | if (device.waitForFences(fences_[curFrame_], true, std::numeric_limits::max()) != vk::Result::eSuccess) { 42 | throw std::runtime_error("wait for fence failed"); 43 | } 44 | device.resetFences(fences_[curFrame_]); 45 | 46 | auto& swapchain = ctx.swapchain; 47 | auto resultValue = device.acquireNextImageKHR(swapchain->swapchain, std::numeric_limits::max(), imageAvaliableSems_[curFrame_], nullptr); 48 | if (resultValue.result != vk::Result::eSuccess) { 49 | throw std::runtime_error("wait for image in swapchain failed"); 50 | } 51 | imageIndex_ = resultValue.value; 52 | 53 | auto& cmdMgr = ctx.commandManager; 54 | auto& cmd = cmdBufs_[curFrame_]; 55 | cmd.reset(); 56 | 57 | vk::CommandBufferBeginInfo beginInfo; 58 | beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); 59 | cmd.begin(beginInfo); 60 | vk::ClearValue clearValue; 61 | clearValue.setColor(vk::ClearColorValue(std::array{0.1, 0.1, 0.1, 1})); 62 | vk::RenderPassBeginInfo renderPassBegin; 63 | renderPassBegin.setRenderPass(ctx.renderProcess->renderPass) 64 | .setFramebuffer(swapchain->framebuffers[imageIndex_]) 65 | .setClearValues(clearValue) 66 | .setRenderArea(vk::Rect2D({}, swapchain->GetExtent())); 67 | cmd.beginRenderPass(&renderPassBegin, vk::SubpassContents::eInline); 68 | } 69 | 70 | void Renderer::DrawTexture(const Rect& rect, Texture& texture) { 71 | auto& ctx = Context::Instance(); 72 | auto& device = ctx.device; 73 | auto& cmd = cmdBufs_[curFrame_]; 74 | vk::DeviceSize offset = 0; 75 | 76 | bufferRectData(); 77 | 78 | cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, ctx.renderProcess->graphicsPipelineWithTriangleTopology); 79 | cmd.bindVertexBuffers(0, rectVerticesBuffer_->buffer, offset); 80 | cmd.bindIndexBuffer(rectIndicesBuffer_->buffer, 0, vk::IndexType::eUint32); 81 | 82 | auto& layout = Context::Instance().renderProcess->layout; 83 | cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, 84 | layout, 85 | 0, {descriptorSets_[curFrame_].set, texture.set.set}, {}); 86 | auto model = Mat4::CreateTranslate(rect.position).Mul(Mat4::CreateScale(rect.size)); 87 | cmd.pushConstants(layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(Mat4), model.GetData()); 88 | cmd.pushConstants(layout, vk::ShaderStageFlagBits::eFragment, sizeof(Mat4), sizeof(Color), &drawColor_); 89 | cmd.drawIndexed(6, 1, 0, 0, 0); 90 | } 91 | 92 | void Renderer::DrawLine(const Vec& p1, const Vec& p2) { 93 | auto& ctx = Context::Instance(); 94 | auto& device = ctx.device; 95 | auto& cmd = cmdBufs_[curFrame_]; 96 | vk::DeviceSize offset = 0; 97 | 98 | bufferLineData(p1, p2); 99 | 100 | cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, ctx.renderProcess->graphicsPipelineWithLineTopology); 101 | cmd.bindVertexBuffers(0, lineVerticesBuffer_->buffer, offset); 102 | 103 | auto& layout = Context::Instance().renderProcess->layout; 104 | cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, 105 | layout, 106 | 0, {descriptorSets_[curFrame_].set, whiteTexture->set.set}, {}); 107 | auto model = Mat4::CreateIdentity(); 108 | cmd.pushConstants(layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(Mat4), model.GetData()); 109 | cmd.pushConstants(layout, vk::ShaderStageFlagBits::eFragment, sizeof(Mat4), sizeof(Color), &drawColor_); 110 | cmd.draw(2, 1, 0, 0); 111 | } 112 | 113 | void Renderer::EndRender() { 114 | auto& ctx = Context::Instance(); 115 | auto& swapchain = ctx.swapchain; 116 | auto& cmd = cmdBufs_[curFrame_]; 117 | cmd.endRenderPass(); 118 | cmd.end(); 119 | 120 | vk::SubmitInfo submit; 121 | vk::PipelineStageFlags flags = vk::PipelineStageFlagBits::eColorAttachmentOutput; 122 | submit.setCommandBuffers(cmd) 123 | .setWaitSemaphores(imageAvaliableSems_[curFrame_]) 124 | .setWaitDstStageMask(flags) 125 | .setSignalSemaphores(renderFinishSems_[curFrame_]); 126 | ctx.graphicsQueue.submit(submit, fences_[curFrame_]); 127 | 128 | vk::PresentInfoKHR presentInfo; 129 | presentInfo.setWaitSemaphores(renderFinishSems_[curFrame_]) 130 | .setSwapchains(swapchain->swapchain) 131 | .setImageIndices(imageIndex_); 132 | if (ctx.presentQueue.presentKHR(presentInfo) != vk::Result::eSuccess) { 133 | throw std::runtime_error("present queue execute failed"); 134 | } 135 | 136 | curFrame_ = (curFrame_ + 1) % maxFlightCount_; 137 | } 138 | 139 | void Renderer::createFences() { 140 | fences_.resize(maxFlightCount_, nullptr); 141 | 142 | for (auto& fence : fences_) { 143 | vk::FenceCreateInfo fenceCreateInfo; 144 | fenceCreateInfo.setFlags(vk::FenceCreateFlagBits::eSignaled); 145 | fence = Context::Instance().device.createFence(fenceCreateInfo); 146 | } 147 | } 148 | 149 | void Renderer::createSemaphores() { 150 | auto& device = Context::Instance().device; 151 | vk::SemaphoreCreateInfo info; 152 | 153 | imageAvaliableSems_.resize(maxFlightCount_); 154 | renderFinishSems_.resize(maxFlightCount_); 155 | 156 | for (auto& sem : imageAvaliableSems_) { 157 | sem = device.createSemaphore(info); 158 | } 159 | 160 | for (auto& sem : renderFinishSems_) { 161 | sem = device.createSemaphore(info); 162 | } 163 | } 164 | 165 | void Renderer::createCmdBuffers() { 166 | cmdBufs_.resize(maxFlightCount_); 167 | 168 | for (auto& cmd : cmdBufs_) { 169 | cmd = Context::Instance().commandManager->CreateOneCommandBuffer(); 170 | } 171 | } 172 | 173 | void Renderer::createBuffers() { 174 | auto& device = Context::Instance().device; 175 | 176 | rectVerticesBuffer_.reset(new Buffer(vk::BufferUsageFlagBits::eVertexBuffer, 177 | sizeof(Vertex) * 4, 178 | vk::MemoryPropertyFlagBits::eHostVisible|vk::MemoryPropertyFlagBits::eHostCoherent)); 179 | 180 | rectIndicesBuffer_.reset(new Buffer(vk::BufferUsageFlagBits::eIndexBuffer, 181 | sizeof(uint32_t) * 6, 182 | vk::MemoryPropertyFlagBits::eHostVisible|vk::MemoryPropertyFlagBits::eHostCoherent)); 183 | 184 | lineVerticesBuffer_.reset(new Buffer(vk::BufferUsageFlagBits::eVertexBuffer, 185 | sizeof(Vertex) * 2, 186 | vk::MemoryPropertyFlagBits::eHostVisible|vk::MemoryPropertyFlagBits::eHostCoherent)); 187 | } 188 | 189 | void Renderer::createUniformBuffers(int flightCount) { 190 | uniformBuffers_.resize(flightCount); 191 | // two mat4 one color 192 | size_t size = sizeof(Mat4) * 2; 193 | for (auto& buffer : uniformBuffers_) { 194 | buffer.reset(new Buffer(vk::BufferUsageFlagBits::eTransferSrc, 195 | size, 196 | vk::MemoryPropertyFlagBits::eHostVisible|vk::MemoryPropertyFlagBits::eHostCoherent)); 197 | } 198 | deviceUniformBuffers_.resize(flightCount); 199 | for (auto& buffer : deviceUniformBuffers_) { 200 | buffer.reset(new Buffer(vk::BufferUsageFlagBits::eTransferDst|vk::BufferUsageFlagBits::eUniformBuffer, 201 | size, 202 | vk::MemoryPropertyFlagBits::eDeviceLocal)); 203 | } 204 | } 205 | 206 | void Renderer::transformBuffer2Device(Buffer& src, Buffer& dst, size_t srcOffset, size_t dstOffset, size_t size) { 207 | Context::Instance().commandManager->ExecuteCmd(Context::Instance().graphicsQueue, 208 | [&](vk::CommandBuffer& cmdBuf) { 209 | vk::BufferCopy region; 210 | region.setSrcOffset(srcOffset) 211 | .setDstOffset(dstOffset) 212 | .setSize(size); 213 | cmdBuf.copyBuffer(src.buffer, dst.buffer, region); 214 | }); 215 | } 216 | 217 | std::uint32_t Renderer::queryBufferMemTypeIndex(std::uint32_t type, vk::MemoryPropertyFlags flag) { 218 | auto property = Context::Instance().phyDevice.getMemoryProperties(); 219 | 220 | for (std::uint32_t i = 0; i < property.memoryTypeCount; i++) { 221 | if ((1 << i) & type && 222 | property.memoryTypes[i].propertyFlags & flag) { 223 | return i; 224 | } 225 | } 226 | 227 | return 0; 228 | } 229 | 230 | void Renderer::bufferRectData() { 231 | bufferRectVertexData(); 232 | bufferRectIndicesData(); 233 | } 234 | 235 | void Renderer::bufferRectVertexData() { 236 | Vertex vertices[] = { 237 | {Vec{-0.5, -0.5},Vec{0, 0}}, 238 | {Vec{0.5, -0.5} ,Vec{1, 0}}, 239 | {Vec{0.5, 0.5} ,Vec{1, 1}}, 240 | {Vec{-0.5, 0.5} ,Vec{0, 1}}, 241 | }; 242 | auto& device = Context::Instance().device; 243 | memcpy(rectVerticesBuffer_->map, vertices, sizeof(vertices)); 244 | } 245 | 246 | void Renderer::bufferRectIndicesData() { 247 | std::uint32_t indices[] = { 248 | 0, 1, 3, 249 | 1, 2, 3, 250 | }; 251 | auto& device = Context::Instance().device; 252 | memcpy(rectIndicesBuffer_->map, indices, sizeof(indices)); 253 | } 254 | 255 | void Renderer::bufferLineData(const Vec& p1, const Vec& p2) { 256 | Vertex vertices[] = { 257 | {p1,Vec{0, 0}}, 258 | {p2,Vec{0, 0}}, 259 | }; 260 | auto& device = Context::Instance().device; 261 | memcpy(lineVerticesBuffer_->map, vertices, sizeof(vertices)); 262 | } 263 | 264 | void Renderer::bufferMVPData() { 265 | struct Matrices { 266 | Mat4 project; 267 | Mat4 view; 268 | } matrices; 269 | auto& device = Context::Instance().device; 270 | for (int i = 0; i < uniformBuffers_.size(); i++) { 271 | auto& buffer = uniformBuffers_[i]; 272 | memcpy(buffer->map, (void*)&projectMat_, sizeof(Mat4)); 273 | memcpy(((float*)buffer->map + 4 * 4), (void*)&viewMat_, sizeof(Mat4)); 274 | transformBuffer2Device(*buffer, *deviceUniformBuffers_[i], 0, 0, buffer->size); 275 | } 276 | } 277 | 278 | void Renderer::SetDrawColor(const Color& color) { 279 | drawColor_ = color; 280 | } 281 | 282 | void Renderer::initMats() { 283 | viewMat_ = Mat4::CreateIdentity(); 284 | projectMat_ = Mat4::CreateIdentity(); 285 | } 286 | 287 | void Renderer::SetProject(int right, int left, int bottom, int top, int far, int near) { 288 | projectMat_ = Mat4::CreateOrtho(left, right, top, bottom, near, far); 289 | bufferMVPData(); 290 | } 291 | 292 | void Renderer::updateDescriptorSets() { 293 | for (int i = 0; i < descriptorSets_.size(); i++) { 294 | // bind MVP buffer 295 | vk::DescriptorBufferInfo bufferInfo1; 296 | bufferInfo1.setBuffer(deviceUniformBuffers_[i]->buffer) 297 | .setOffset(0) 298 | .setRange(sizeof(Mat4) * 2); 299 | 300 | std::vector writeInfos(1); 301 | writeInfos[0].setBufferInfo(bufferInfo1) 302 | .setDstBinding(0) 303 | .setDescriptorType(vk::DescriptorType::eUniformBuffer) 304 | .setDescriptorCount(1) 305 | .setDstArrayElement(0) 306 | .setDstSet(descriptorSets_[i].set); 307 | 308 | Context::Instance().device.updateDescriptorSets(writeInfos, {}); 309 | } 310 | } 311 | 312 | void Renderer::createWhiteTexture() { 313 | unsigned char data[] = {0xFF, 0xFF, 0xFF, 0xFF}; 314 | whiteTexture = TextureManager::Instance().Create((void*)data, 1, 1); 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/shader.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/shader.hpp" 2 | #include "toy2d/context.hpp" 3 | #include "toy2d/math.hpp" 4 | 5 | namespace toy2d { 6 | 7 | Shader::Shader(const std::vector& vertexSource, const std::vector& fragSource) { 8 | vk::ShaderModuleCreateInfo vertexModuleCreateInfo, fragModuleCreateInfo; 9 | vertexModuleCreateInfo.codeSize = vertexSource.size(); 10 | vertexModuleCreateInfo.pCode = (std::uint32_t*)vertexSource.data(); 11 | fragModuleCreateInfo.codeSize = fragSource.size(); 12 | fragModuleCreateInfo.pCode = (std::uint32_t*)fragSource.data(); 13 | 14 | auto& device = Context::Instance().device; 15 | vertexModule_ = device.createShaderModule(vertexModuleCreateInfo); 16 | fragModule_ = device.createShaderModule(fragModuleCreateInfo); 17 | 18 | initDescriptorSetLayouts(); 19 | } 20 | 21 | void Shader::initDescriptorSetLayouts() { 22 | vk::DescriptorSetLayoutCreateInfo createInfo; 23 | std::vector bindings(1); 24 | bindings[0].setBinding(0) 25 | .setDescriptorCount(1) 26 | .setDescriptorType(vk::DescriptorType::eUniformBuffer) 27 | .setStageFlags(vk::ShaderStageFlagBits::eVertex); 28 | createInfo.setBindings(bindings); 29 | 30 | layouts_.push_back(Context::Instance().device.createDescriptorSetLayout(createInfo)); 31 | 32 | bindings.resize(1); 33 | bindings[0].setBinding(0) 34 | .setDescriptorCount(1) 35 | .setDescriptorType(vk::DescriptorType::eCombinedImageSampler) 36 | .setStageFlags(vk::ShaderStageFlagBits::eFragment); 37 | createInfo.setBindings(bindings); 38 | 39 | layouts_.push_back(Context::Instance().device.createDescriptorSetLayout(createInfo)); 40 | } 41 | 42 | Shader::~Shader() { 43 | auto& device = Context::Instance().device; 44 | for (auto& layout : layouts_) { 45 | device.destroyDescriptorSetLayout(layout); 46 | } 47 | layouts_.clear(); 48 | device.destroyShaderModule(vertexModule_); 49 | device.destroyShaderModule(fragModule_); 50 | } 51 | 52 | std::vector Shader::GetPushConstantRange() const { 53 | std::vector ranges(2); 54 | ranges[0].setOffset(0) 55 | .setSize(sizeof(Mat4)) 56 | .setStageFlags(vk::ShaderStageFlagBits::eVertex); 57 | ranges[1].setOffset(sizeof(Mat4)) 58 | .setSize(sizeof(Color)) 59 | .setStageFlags(vk::ShaderStageFlagBits::eFragment); 60 | return ranges; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/swapchain.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/swapchain.hpp" 2 | #include "toy2d/context.hpp" 3 | 4 | namespace toy2d { 5 | 6 | Swapchain::Swapchain(vk::SurfaceKHR surface, int windowWidth, int windowHeight): surface(surface) { 7 | querySurfaceInfo(windowWidth, windowHeight); 8 | swapchain = createSwapchain(); 9 | createImageAndViews(); 10 | } 11 | 12 | Swapchain::~Swapchain() { 13 | auto& ctx = Context::Instance(); 14 | for (auto& img : images) { 15 | ctx.device.destroyImageView(img.view); 16 | } 17 | for (auto& framebuffer : framebuffers) { 18 | Context::Instance().device.destroyFramebuffer(framebuffer); 19 | } 20 | ctx.device.destroySwapchainKHR(swapchain); 21 | ctx.instance.destroySurfaceKHR(surface); 22 | } 23 | 24 | void Swapchain::InitFramebuffers() { 25 | createFramebuffers(); 26 | } 27 | 28 | void Swapchain::querySurfaceInfo(int windowWidth, int windowHeight) { 29 | surfaceInfo_.format = querySurfaceeFormat(); 30 | 31 | auto capability = Context::Instance().phyDevice.getSurfaceCapabilitiesKHR(surface); 32 | surfaceInfo_.count = std::clamp(capability.minImageCount + 1, 33 | capability.minImageCount, capability.maxImageCount); 34 | surfaceInfo_.transform = capability.currentTransform; 35 | surfaceInfo_.extent = querySurfaceExtent(capability, windowWidth, windowHeight); 36 | } 37 | 38 | vk::SurfaceFormatKHR Swapchain::querySurfaceeFormat() { 39 | auto formats = Context::Instance().phyDevice.getSurfaceFormatsKHR(surface); 40 | for (auto& format : formats) { 41 | if (format.format == vk::Format::eR8G8B8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { 42 | return format; 43 | } 44 | } 45 | return formats[0]; 46 | } 47 | 48 | vk::Extent2D Swapchain::querySurfaceExtent(const vk::SurfaceCapabilitiesKHR& capability, int windowWidth, int windowHeight) { 49 | if (capability.currentExtent.width != std::numeric_limits::max()) { 50 | return capability.currentExtent; 51 | } else { 52 | auto extent = vk::Extent2D{ 53 | static_cast(windowWidth), 54 | static_cast(windowHeight) 55 | }; 56 | 57 | extent.width = std::clamp(extent.width, capability.minImageExtent.width, capability.maxImageExtent.width); 58 | extent.height = std::clamp(extent.height, capability.minImageExtent.height, capability.maxImageExtent.height); 59 | return extent; 60 | } 61 | } 62 | 63 | vk::SwapchainKHR Swapchain::createSwapchain() { 64 | vk::SwapchainCreateInfoKHR createInfo; 65 | createInfo.setClipped(true) 66 | .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque) 67 | .setImageExtent(surfaceInfo_.extent) 68 | .setImageColorSpace(surfaceInfo_.format.colorSpace) 69 | .setImageFormat(surfaceInfo_.format.format) 70 | .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment) 71 | .setMinImageCount(surfaceInfo_.count) 72 | .setImageArrayLayers(1) 73 | .setPresentMode(vk::PresentModeKHR::eFifo) 74 | .setPreTransform(surfaceInfo_.transform) 75 | .setSurface(surface); 76 | 77 | auto& ctx = Context::Instance(); 78 | if (ctx.queueInfo.graphicsIndex.value() == ctx.queueInfo.presentIndex.value()) { 79 | createInfo.setImageSharingMode(vk::SharingMode::eExclusive); 80 | } else { 81 | createInfo.setImageSharingMode(vk::SharingMode::eConcurrent); 82 | std::array queueIndices = {ctx.queueInfo.graphicsIndex.value(), ctx.queueInfo.presentIndex.value()}; 83 | createInfo.setQueueFamilyIndices(queueIndices); 84 | } 85 | 86 | return ctx.device.createSwapchainKHR(createInfo); 87 | } 88 | 89 | void Swapchain::createImageAndViews() { 90 | auto& ctx = Context::Instance(); 91 | auto images = ctx.device.getSwapchainImagesKHR(swapchain); 92 | for (auto& image : images) { 93 | Image img; 94 | img.image = image; 95 | vk::ImageViewCreateInfo viewCreateInfo; 96 | vk::ImageSubresourceRange range; 97 | range.setBaseArrayLayer(0) 98 | .setBaseMipLevel(0) 99 | .setLayerCount(1) 100 | .setLevelCount(1) 101 | .setAspectMask(vk::ImageAspectFlagBits::eColor); 102 | viewCreateInfo.setImage(image) 103 | .setFormat(surfaceInfo_.format.format) 104 | .setViewType(vk::ImageViewType::e2D) 105 | .setSubresourceRange(range) 106 | .setComponents(vk::ComponentMapping{}); 107 | img.view = ctx.device.createImageView(viewCreateInfo); 108 | this->images.push_back(img); 109 | } 110 | } 111 | 112 | void Swapchain::createFramebuffers() { 113 | for (auto& img : images) { 114 | auto& view = img.view; 115 | 116 | vk::FramebufferCreateInfo createInfo; 117 | createInfo.setAttachments(view) 118 | .setLayers(1) 119 | .setHeight(GetExtent().height) 120 | .setWidth(GetExtent().width) 121 | .setRenderPass(Context::Instance().renderProcess->renderPass); 122 | 123 | framebuffers.push_back(Context::Instance().device.createFramebuffer(createInfo)); 124 | } 125 | } 126 | 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/texture.hpp" 2 | 3 | #define STB_IMAGE_IMPLEMENTATION 4 | #include "toy2d/stb_image.h" 5 | 6 | namespace toy2d { 7 | 8 | Texture::Texture(std::string_view filename) { 9 | int w, h, channel; 10 | stbi_uc* pixels = stbi_load(filename.data(), &w, &h, &channel, STBI_rgb_alpha); 11 | size_t size = w * h * 4; 12 | 13 | if (!pixels) { 14 | throw std::runtime_error("image load failed"); 15 | } 16 | 17 | init(pixels, w, h); 18 | 19 | stbi_image_free(pixels); 20 | } 21 | 22 | Texture::Texture(void* data, unsigned int w, unsigned int h) { 23 | init(data, w, h); 24 | } 25 | 26 | void Texture::init(void* data, uint32_t w, uint32_t h) { 27 | const uint32_t size = w * h * 4; 28 | std::unique_ptr buffer(new Buffer(vk::BufferUsageFlagBits::eTransferSrc, 29 | size, 30 | vk::MemoryPropertyFlagBits::eHostCoherent|vk::MemoryPropertyFlagBits::eHostVisible)); 31 | memcpy(buffer->map, data, size); 32 | 33 | createImage(w, h); 34 | allocMemory(); 35 | Context::Instance().device.bindImageMemory(image, memory, 0); 36 | 37 | transitionImageLayoutFromUndefine2Dst(); 38 | transformData2Image(*buffer, w, h); 39 | transitionImageLayoutFromDst2Optimal(); 40 | 41 | createImageView(); 42 | 43 | set = DescriptorSetManager::Instance().AllocImageSet(); 44 | 45 | updateDescriptorSet(); 46 | } 47 | 48 | Texture::~Texture() { 49 | auto& device = Context::Instance().device; 50 | DescriptorSetManager::Instance().FreeImageSet(set); 51 | device.destroyImageView(view); 52 | device.freeMemory(memory); 53 | device.destroyImage(image); 54 | } 55 | 56 | void Texture::createImage(uint32_t w, uint32_t h) { 57 | vk::ImageCreateInfo createInfo; 58 | createInfo.setImageType(vk::ImageType::e2D) 59 | .setArrayLayers(1) 60 | .setMipLevels(1) 61 | .setExtent({w, h, 1}) 62 | .setFormat(vk::Format::eR8G8B8A8Srgb) 63 | .setTiling(vk::ImageTiling::eOptimal) 64 | .setInitialLayout(vk::ImageLayout::eUndefined) 65 | .setUsage(vk::ImageUsageFlagBits::eTransferDst|vk::ImageUsageFlagBits::eSampled) 66 | .setSamples(vk::SampleCountFlagBits::e1); 67 | image = Context::Instance().device.createImage(createInfo); 68 | } 69 | 70 | void Texture::allocMemory() { 71 | auto& device = Context::Instance().device; 72 | vk::MemoryAllocateInfo allocInfo; 73 | 74 | auto requirements = device.getImageMemoryRequirements(image); 75 | allocInfo.setAllocationSize(requirements.size); 76 | 77 | auto index = QueryBufferMemTypeIndex(requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal); 78 | allocInfo.setMemoryTypeIndex(index); 79 | 80 | memory = device.allocateMemory(allocInfo); 81 | } 82 | 83 | void Texture::transformData2Image(Buffer& buffer, uint32_t w, uint32_t h) { 84 | Context::Instance().commandManager->ExecuteCmd(Context::Instance().graphicsQueue, 85 | [&](vk::CommandBuffer cmdBuf){ 86 | vk::BufferImageCopy region; 87 | vk::ImageSubresourceLayers subsource; 88 | subsource.setAspectMask(vk::ImageAspectFlagBits::eColor) 89 | .setBaseArrayLayer(0) 90 | .setMipLevel(0) 91 | .setLayerCount(1); 92 | region.setBufferImageHeight(0) 93 | .setBufferOffset(0) 94 | .setImageOffset(0) 95 | .setImageExtent({w, h, 1}) 96 | .setBufferRowLength(0) 97 | .setImageSubresource(subsource); 98 | cmdBuf.copyBufferToImage(buffer.buffer, image, 99 | vk::ImageLayout::eTransferDstOptimal, 100 | region); 101 | }); 102 | } 103 | 104 | void Texture::transitionImageLayoutFromUndefine2Dst() { 105 | Context::Instance().commandManager->ExecuteCmd(Context::Instance().graphicsQueue, 106 | [&](vk::CommandBuffer cmdBuf){ 107 | vk::ImageMemoryBarrier barrier; 108 | vk::ImageSubresourceRange range; 109 | range.setLayerCount(1) 110 | .setBaseArrayLayer(0) 111 | .setLevelCount(1) 112 | .setBaseMipLevel(0) 113 | .setAspectMask(vk::ImageAspectFlagBits::eColor); 114 | barrier.setImage(image) 115 | .setOldLayout(vk::ImageLayout::eUndefined) 116 | .setNewLayout(vk::ImageLayout::eTransferDstOptimal) 117 | .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 118 | .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 119 | .setDstAccessMask((vk::AccessFlagBits::eTransferWrite)) 120 | .setSubresourceRange(range); 121 | cmdBuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, 122 | {}, {}, nullptr, barrier); 123 | }); 124 | } 125 | 126 | void Texture::transitionImageLayoutFromDst2Optimal() { 127 | Context::Instance().commandManager->ExecuteCmd(Context::Instance().graphicsQueue, 128 | [&](vk::CommandBuffer cmdBuf){ 129 | vk::ImageMemoryBarrier barrier; 130 | vk::ImageSubresourceRange range; 131 | range.setLayerCount(1) 132 | .setBaseArrayLayer(0) 133 | .setLevelCount(1) 134 | .setBaseMipLevel(0) 135 | .setAspectMask(vk::ImageAspectFlagBits::eColor); 136 | barrier.setImage(image) 137 | .setOldLayout(vk::ImageLayout::eTransferDstOptimal) 138 | .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) 139 | .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 140 | .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 141 | .setSrcAccessMask((vk::AccessFlagBits::eTransferWrite)) 142 | .setDstAccessMask((vk::AccessFlagBits::eShaderRead)) 143 | .setSubresourceRange(range); 144 | cmdBuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, 145 | {}, {}, nullptr, barrier); 146 | }); 147 | } 148 | 149 | void Texture::createImageView() { 150 | vk::ImageViewCreateInfo createInfo; 151 | vk::ComponentMapping mapping; 152 | vk::ImageSubresourceRange range; 153 | range.setAspectMask(vk::ImageAspectFlagBits::eColor) 154 | .setBaseArrayLayer(0) 155 | .setLayerCount(1) 156 | .setLevelCount(1) 157 | .setBaseMipLevel(0); 158 | createInfo.setImage(image) 159 | .setViewType(vk::ImageViewType::e2D) 160 | .setComponents(mapping) 161 | .setFormat(vk::Format::eR8G8B8A8Srgb) 162 | .setSubresourceRange(range); 163 | view = Context::Instance().device.createImageView(createInfo); 164 | } 165 | 166 | void Texture::updateDescriptorSet() { 167 | vk::WriteDescriptorSet writer; 168 | vk::DescriptorImageInfo imageInfo; 169 | imageInfo.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal) 170 | .setImageView(view) 171 | .setSampler(Context::Instance().sampler); 172 | writer.setImageInfo(imageInfo) 173 | .setDstBinding(0) 174 | .setDstArrayElement(0) 175 | .setDstSet(set.set) 176 | .setDescriptorCount(1) 177 | .setDescriptorType(vk::DescriptorType::eCombinedImageSampler); 178 | Context::Instance().device.updateDescriptorSets(writer, {}); 179 | } 180 | 181 | std::unique_ptr TextureManager::instance_ = nullptr; 182 | 183 | Texture* TextureManager::Load(const std::string& filename) { 184 | datas_.push_back(std::unique_ptr(new Texture(filename))); 185 | return datas_.back().get(); 186 | } 187 | 188 | Texture* TextureManager::Create(void* data, uint32_t w, uint32_t h) { 189 | datas_.push_back(std::unique_ptr(new Texture(data, w, h))); 190 | return datas_.back().get(); 191 | } 192 | 193 | void TextureManager::Clear() { 194 | datas_.clear(); 195 | } 196 | 197 | void TextureManager::Destroy(Texture* texture) { 198 | auto it = std::find_if(datas_.begin(), datas_.end(), 199 | [&](const std::unique_ptr& t) { 200 | return t.get() == texture; 201 | }); 202 | if (it != datas_.end()) { 203 | Context::Instance().device.waitIdle(); 204 | datas_.erase(it); 205 | return; 206 | } 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /src/tool.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/tool.hpp" 2 | 3 | namespace toy2d { 4 | 5 | std::vector ReadWholeFile(const std::string& filename) { 6 | std::ifstream file(filename, std::ios::binary|std::ios::ate); 7 | 8 | if (!file.is_open()) { 9 | std::cout << "read " << filename << " failed" << std::endl; 10 | return std::vector{}; 11 | } 12 | 13 | auto size = file.tellg(); 14 | std::vector content(size); 15 | file.seekg(0); 16 | 17 | file.read(content.data(), content.size()); 18 | 19 | return content; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/toy2d.cpp: -------------------------------------------------------------------------------- 1 | #include "toy2d/toy2d.hpp" 2 | 3 | namespace toy2d { 4 | 5 | std::unique_ptr renderer_; 6 | 7 | void Init(std::vector& extensions, Context::GetSurfaceCallback cb, int windowWidth, int windowHeight) { 8 | Context::Init(extensions, cb); 9 | auto& ctx = Context::Instance(); 10 | ctx.initSwapchain(windowWidth, windowHeight); 11 | ctx.initShaderModules(); 12 | ctx.initRenderProcess(); 13 | ctx.initGraphicsPipeline(); 14 | ctx.swapchain->InitFramebuffers(); 15 | ctx.initCommandPool(); 16 | ctx.initSampler(); 17 | 18 | int maxFlightCount = 2; 19 | DescriptorSetManager::Init(maxFlightCount); 20 | renderer_ = std::make_unique(maxFlightCount); 21 | renderer_->SetProject(windowWidth, 0, 0, windowHeight, -1, 1); 22 | } 23 | 24 | void Quit() { 25 | Context::Instance().device.waitIdle(); 26 | renderer_.reset(); 27 | TextureManager::Instance().Clear(); 28 | DescriptorSetManager::Quit(); 29 | Context::Quit(); 30 | } 31 | 32 | Texture* LoadTexture(const std::string& filename) { 33 | return TextureManager::Instance().Load(filename); 34 | } 35 | 36 | void DestroyTexture(Texture* texture) { 37 | TextureManager::Instance().Destroy(texture); 38 | } 39 | 40 | Renderer* GetRenderer() { 41 | return renderer_.get(); 42 | } 43 | 44 | void ResizeSwapchainImage(int w, int h) { 45 | auto& ctx = Context::Instance(); 46 | ctx.device.waitIdle(); 47 | 48 | ctx.swapchain.reset(); 49 | ctx.getSurface(); 50 | ctx.initSwapchain(w, h); 51 | ctx.swapchain->InitFramebuffers(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /toy2d/buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "toy2d/context.hpp" 4 | 5 | namespace toy2d { 6 | 7 | struct Buffer { 8 | vk::Buffer buffer; 9 | vk::DeviceMemory memory; 10 | void* map; 11 | size_t size; 12 | size_t requireSize; 13 | 14 | Buffer(vk::BufferUsageFlags usage, size_t size, vk::MemoryPropertyFlags memProperty); 15 | ~Buffer(); 16 | 17 | Buffer(const Buffer&) = delete; 18 | Buffer& operator=(const Buffer&) = delete; 19 | }; 20 | 21 | std::uint32_t QueryBufferMemTypeIndex(std::uint32_t requirementBit, vk::MemoryPropertyFlags); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /toy2d/command_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include 5 | 6 | namespace toy2d { 7 | 8 | class CommandManager final { 9 | public: 10 | CommandManager(); 11 | ~CommandManager(); 12 | 13 | vk::CommandBuffer CreateOneCommandBuffer(); 14 | std::vector CreateCommandBuffers(std::uint32_t count); 15 | void ResetCmds(); 16 | void FreeCmd(const vk::CommandBuffer&); 17 | 18 | using RecordCmdFunc = std::function; 19 | void ExecuteCmd(vk::Queue, RecordCmdFunc); 20 | 21 | private: 22 | vk::CommandPool pool_; 23 | 24 | vk::CommandPool createCommandPool(); 25 | }; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /toy2d/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "vulkan/vulkan.hpp" 11 | #include "swapchain.hpp" 12 | #include "render_process.hpp" 13 | #include "tool.hpp" 14 | #include "command_manager.hpp" 15 | #include "shader.hpp" 16 | 17 | namespace toy2d { 18 | 19 | class Context { 20 | public: 21 | using GetSurfaceCallback = std::function; 22 | friend void Init(std::vector&, GetSurfaceCallback, int, int); 23 | friend void ResizeSwapchainImage(int w, int h); 24 | 25 | static void Init(std::vector& extensions, GetSurfaceCallback); 26 | static void Quit(); 27 | static Context& Instance(); 28 | 29 | struct QueueInfo { 30 | std::optional graphicsIndex; 31 | std::optional presentIndex; 32 | } queueInfo; 33 | 34 | vk::Instance instance; 35 | vk::PhysicalDevice phyDevice; 36 | vk::Device device; 37 | vk::Queue graphicsQueue; 38 | vk::Queue presentQueue; 39 | std::unique_ptr swapchain; 40 | std::unique_ptr renderProcess; 41 | std::unique_ptr commandManager; 42 | std::unique_ptr shader; 43 | vk::Sampler sampler; 44 | 45 | private: 46 | static Context* instance_; 47 | vk::SurfaceKHR surface_ = nullptr; 48 | 49 | GetSurfaceCallback getSurfaceCb_ = nullptr; 50 | 51 | Context(std::vector& extensions, GetSurfaceCallback); 52 | ~Context(); 53 | 54 | void initRenderProcess(); 55 | void initSwapchain(int windowWidth, int windowHeight); 56 | void initGraphicsPipeline(); 57 | void initCommandPool(); 58 | void initShaderModules(); 59 | void initSampler(); 60 | void getSurface(); 61 | 62 | vk::Instance createInstance(std::vector& extensions); 63 | vk::PhysicalDevice pickupPhysicalDevice(); 64 | vk::Device createDevice(vk::SurfaceKHR); 65 | 66 | void queryQueueInfo(vk::SurfaceKHR); 67 | }; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /toy2d/descriptor_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include 5 | #include 6 | 7 | namespace toy2d { 8 | 9 | class DescriptorSetManager final { 10 | public: 11 | struct SetInfo { 12 | vk::DescriptorSet set; 13 | vk::DescriptorPool pool; 14 | }; 15 | 16 | static void Init(uint32_t maxFlight) { 17 | instance_.reset(new DescriptorSetManager(maxFlight)); 18 | } 19 | 20 | static void Quit() { 21 | instance_.reset(); 22 | } 23 | 24 | static DescriptorSetManager& Instance() { 25 | return *instance_; 26 | } 27 | 28 | DescriptorSetManager(uint32_t maxFlight); 29 | ~DescriptorSetManager(); 30 | 31 | std::vector AllocBufferSets(uint32_t num); 32 | SetInfo AllocImageSet(); 33 | 34 | void FreeImageSet(const SetInfo&); 35 | 36 | private: 37 | struct PoolInfo { 38 | vk::DescriptorPool pool_; 39 | uint32_t remainNum_; 40 | }; 41 | 42 | PoolInfo bufferSetPool_; 43 | 44 | std::vector fulledImageSetPool_; 45 | std::vector avalibleImageSetPool_; 46 | 47 | void addImageSetPool(); 48 | PoolInfo& getAvaliableImagePoolInfo(); 49 | 50 | uint32_t maxFlight_; 51 | 52 | static std::unique_ptr instance_; 53 | }; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /toy2d/math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include 5 | 6 | namespace toy2d { 7 | 8 | struct Vec final { 9 | union { 10 | struct { float x, y; }; 11 | struct { float w, h; }; 12 | }; 13 | 14 | static std::vector GetAttributeDescription(); 15 | static std::vector GetBindingDescription(); 16 | }; 17 | 18 | struct Vertex final { 19 | Vec position; 20 | Vec texcoord; 21 | }; 22 | 23 | struct Color final { 24 | float r, g, b; 25 | }; 26 | 27 | using Size = Vec; 28 | 29 | class Mat4 { 30 | public: 31 | static Mat4 CreateIdentity(); 32 | static Mat4 CreateOnes(); 33 | static Mat4 CreateOrtho(int left, int right, int top, int bottom, int near, int far); 34 | static Mat4 CreateTranslate(const Vec&); 35 | static Mat4 CreateScale(const Vec&); 36 | static Mat4 Create(const std::initializer_list&); 37 | 38 | Mat4(); 39 | const float* GetData() const { return data_; } 40 | void Set(int x, int y, float value) { 41 | data_[x * 4 + y] = value; 42 | } 43 | float Get(int x, int y) const { 44 | return data_[x * 4 + y]; 45 | } 46 | 47 | Mat4 Mul(const Mat4& m) const; 48 | 49 | private: 50 | float data_[4 * 4]; 51 | }; 52 | 53 | struct Rect { 54 | Vec position; 55 | Size size; 56 | }; 57 | 58 | } 59 | -------------------------------------------------------------------------------- /toy2d/render_process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include "toy2d/shader.hpp" 5 | #include 6 | 7 | namespace toy2d { 8 | 9 | class RenderProcess { 10 | public: 11 | vk::Pipeline graphicsPipelineWithTriangleTopology = nullptr; 12 | vk::Pipeline graphicsPipelineWithLineTopology = nullptr; 13 | vk::RenderPass renderPass = nullptr; 14 | vk::PipelineLayout layout = nullptr; 15 | 16 | RenderProcess(); 17 | ~RenderProcess(); 18 | 19 | void CreateGraphicsPipeline(const Shader& shader); 20 | void CreateRenderPass(); 21 | 22 | private: 23 | vk::PipelineCache pipelineCache_ = nullptr; 24 | 25 | vk::PipelineLayout createLayout(); 26 | vk::Pipeline createGraphicsPipeline(const Shader& shader, vk::PrimitiveTopology); 27 | vk::RenderPass createRenderPass(); 28 | vk::PipelineCache createPipelineCache(); 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /toy2d/renderer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include "toy2d/context.hpp" 5 | #include "toy2d/command_manager.hpp" 6 | #include "toy2d/swapchain.hpp" 7 | #include "toy2d/math.hpp" 8 | #include "toy2d/buffer.hpp" 9 | #include "toy2d/texture.hpp" 10 | #include 11 | 12 | namespace toy2d { 13 | 14 | class Renderer { 15 | public: 16 | Renderer(int maxFlightCount); 17 | ~Renderer(); 18 | 19 | void SetProject(int right, int left, int bottom, int top, int far, int near); 20 | void DrawTexture(const Rect&, Texture& texture); 21 | void DrawLine(const Vec& p1, const Vec& p2); 22 | void SetDrawColor(const Color&); 23 | 24 | void StartRender(); 25 | void EndRender(); 26 | 27 | private: 28 | int maxFlightCount_; 29 | int curFrame_; 30 | uint32_t imageIndex_; 31 | std::vector fences_; 32 | std::vector imageAvaliableSems_; 33 | std::vector renderFinishSems_; 34 | std::vector cmdBufs_; 35 | std::unique_ptr rectVerticesBuffer_; 36 | std::unique_ptr rectIndicesBuffer_; 37 | std::unique_ptr lineVerticesBuffer_; 38 | Mat4 projectMat_; 39 | Mat4 viewMat_; 40 | std::vector> uniformBuffers_; 41 | std::vector> deviceUniformBuffers_; 42 | std::vector descriptorSets_; 43 | vk::Sampler sampler; 44 | Texture* whiteTexture; 45 | Color drawColor_ = {1, 1, 1}; 46 | 47 | void createFences(); 48 | void createSemaphores(); 49 | void createCmdBuffers(); 50 | void createBuffers(); 51 | void createUniformBuffers(int flightCount); 52 | 53 | void bufferRectData(); 54 | void bufferRectVertexData(); 55 | void bufferRectIndicesData(); 56 | 57 | void bufferLineData(const Vec& p1, const Vec& p2); 58 | 59 | void bufferMVPData(); 60 | void initMats(); 61 | void updateDescriptorSets(); 62 | void transformBuffer2Device(Buffer& src, Buffer& dst, size_t srcOffset, size_t dstOffset, size_t size); 63 | void createWhiteTexture(); 64 | 65 | std::uint32_t queryBufferMemTypeIndex(std::uint32_t, vk::MemoryPropertyFlags); 66 | }; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /toy2d/shader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include 5 | 6 | namespace toy2d { 7 | 8 | class Shader { 9 | public: 10 | Shader(const std::vector& vertexSource, const std::vector& fragSource); 11 | ~Shader(); 12 | 13 | vk::ShaderModule GetVertexModule() const { return vertexModule_; } 14 | vk::ShaderModule GetFragModule() const { return fragModule_; } 15 | 16 | const std::vector& GetDescriptorSetLayouts() const { return layouts_; } 17 | std::vector GetPushConstantRange() const; 18 | 19 | private: 20 | vk::ShaderModule vertexModule_; 21 | vk::ShaderModule fragModule_; 22 | std::vector layouts_; 23 | 24 | void initDescriptorSetLayouts(); 25 | }; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /toy2d/swapchain.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | 5 | namespace toy2d { 6 | 7 | class Swapchain final { 8 | public: 9 | struct Image { 10 | vk::Image image; 11 | vk::ImageView view; 12 | }; 13 | 14 | vk::SurfaceKHR surface = nullptr; 15 | vk::SwapchainKHR swapchain = nullptr; 16 | std::vector images; 17 | std::vector framebuffers; 18 | 19 | const auto& GetExtent() const { return surfaceInfo_.extent; } 20 | const auto& GetFormat() const { return surfaceInfo_.format; } 21 | 22 | Swapchain(vk::SurfaceKHR, int windowWidth, int windowHeight); 23 | ~Swapchain(); 24 | 25 | void InitFramebuffers(); 26 | 27 | private: 28 | struct SurfaceInfo { 29 | vk::SurfaceFormatKHR format; 30 | vk::Extent2D extent; 31 | std::uint32_t count; 32 | vk::SurfaceTransformFlagBitsKHR transform; 33 | } surfaceInfo_; 34 | 35 | vk::SwapchainKHR createSwapchain(); 36 | 37 | void querySurfaceInfo(int windowWidth, int windowHeight); 38 | vk::SurfaceFormatKHR querySurfaceeFormat(); 39 | vk::Extent2D querySurfaceExtent(const vk::SurfaceCapabilitiesKHR& capability, int windowWidth, int windowHeight); 40 | void createImageAndViews(); 41 | void createFramebuffers(); 42 | }; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /toy2d/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulkan/vulkan.hpp" 4 | #include "buffer.hpp" 5 | #include "descriptor_manager.hpp" 6 | #include 7 | #include 8 | 9 | namespace toy2d { 10 | 11 | class TextureManager; 12 | 13 | class Texture final { 14 | public: 15 | friend class TextureManager; 16 | ~Texture(); 17 | 18 | vk::Image image; 19 | vk::DeviceMemory memory; 20 | vk::ImageView view; 21 | DescriptorSetManager::SetInfo set; 22 | 23 | private: 24 | Texture(std::string_view filename); 25 | 26 | Texture(void* data, uint32_t w, uint32_t h); 27 | 28 | void createImage(uint32_t w, uint32_t h); 29 | void createImageView(); 30 | void allocMemory(); 31 | uint32_t queryImageMemoryIndex(); 32 | void transitionImageLayoutFromUndefine2Dst(); 33 | void transitionImageLayoutFromDst2Optimal(); 34 | void transformData2Image(Buffer&, uint32_t w, uint32_t h); 35 | void updateDescriptorSet(); 36 | 37 | void init(void* data, uint32_t w, uint32_t h); 38 | }; 39 | 40 | class TextureManager final { 41 | public: 42 | static TextureManager& Instance() { 43 | if (!instance_) { 44 | instance_.reset(new TextureManager); 45 | } 46 | return *instance_; 47 | } 48 | 49 | Texture* Load(const std::string& filename); 50 | 51 | // data must be a RGBA8888 format data 52 | Texture* Create(void* data, uint32_t w, uint32_t h); 53 | void Destroy(Texture*); 54 | void Clear(); 55 | 56 | private: 57 | static std::unique_ptr instance_; 58 | 59 | std::vector> datas_; 60 | }; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /toy2d/tool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace toy2d { 8 | 9 | std::vector ReadWholeFile(const std::string& filename); 10 | 11 | } -------------------------------------------------------------------------------- /toy2d/toy2d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "context.hpp" 4 | #include "render_process.hpp" 5 | #include "renderer.hpp" 6 | #include "descriptor_manager.hpp" 7 | #include 8 | 9 | namespace toy2d { 10 | 11 | void Init(std::vector& extensions, Context::GetSurfaceCallback, int windowWidth, int windowHeight); 12 | void Quit(); 13 | Texture* LoadTexture(const std::string& filename); 14 | void DestroyTexture(Texture*); 15 | void ResizeSwapchainImage(int w, int h); 16 | Renderer* GetRenderer(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /welcome.txt: -------------------------------------------------------------------------------- 1 | ____ ____ __ __ __ __ ___ ___ .__ __. 2 | \ \ / / | | | | | | | |/ / / \ | \ | | 3 | \ \/ / | | | | | | | ' / / ^ \ | \| | 4 | \ / | | | | | | | < / /_\ \ | . ` | 5 | \ / | `--' | | `----.| . \ / _____ \ | |\ | 6 | \__/ \______/ |_______||__|\__\ /__/ \__\ |__| \__| 7 | 8 | .___________. __ __ .___________. ______ .______ __ ___ __ 9 | | || | | | | | / __ \ | _ \ | | / \ | | 10 | `---| |----`| | | | `---| |----`| | | | | |_) | | | / ^ \ | | 11 | | | | | | | | | | | | | | / | | / /_\ \ | | 12 | | | | `--' | | | | `--' | | |\ \----.| | / _____ \ | `----. 13 | |__| \______/ |__| \______/ | _| `._____||__| /__/ \__\ |_______| 14 | --------------------------------------------------------------------------------