vulkan/src/main.cpp

1556 lines
59 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include "vulkan/vulkan.h"
#else
#include "volk/volk.h"
#endif
#include "vulkan/vk_enum_string_helper.h"
#include "SDL3/SDL.h"
#include "SDL3/SDL_vulkan.h"
#include "directxmath/directxmath.h"
#include "check.h"
#include "new.h"
#include "file.h"
#include "dds/validate.h"
#include "vulkan_helper.h"
#include "shader_data.h"
#include "minmax.h"
#include "view.h"
#include "font/outline.h"
#include "renpy/vulkan.h"
#include "renpy/composite/vulkan.h"
#include "renpy/interpreter.h"
#include "renpy/interact.h"
#include "renpy/script.h"
#include "audio.h"
VkInstance instance{ VK_NULL_HANDLE };
VkDevice device{ VK_NULL_HANDLE };
VkQueue queue{ VK_NULL_HANDLE };
VkSurfaceKHR surface{ VK_NULL_HANDLE };
VkSwapchainKHR swapchain{ VK_NULL_HANDLE };
uint32_t swapchainImageCount{ 0 };
VkImage * swapchainImages{ nullptr };
VkImageView * swapchainImageViews{ nullptr };
VkImage depthImage{ VK_NULL_HANDLE };
VkImageView depthImageView{ VK_NULL_HANDLE };
VkDeviceMemory depthMemory{ VK_NULL_HANDLE };
constexpr int colorImageCount = 3;
VkImage colorImage[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
VkImageView colorImageView[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
VkDeviceMemory colorMemory[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
uint32_t shadowArrayLayers{ 6 };
VkImage shadowDepthImage{ VK_NULL_HANDLE };
VkImageView shadowDepthImageView{ VK_NULL_HANDLE };
VkImageView shadowDepthImageViewDepth{ VK_NULL_HANDLE };
VkDeviceMemory shadowDepthMemory{ VK_NULL_HANDLE };
VkFence fences[maxFramesInFlight];
VkSemaphore presentSemaphores[maxFramesInFlight];
VkSemaphore * renderSemaphores{ nullptr };
VkCommandPool commandPool{ VK_NULL_HANDLE };
VkCommandBuffer commandBuffers[maxFramesInFlight];
VkSampler textureSamplers[3]{ VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE };
VkDescriptorPool descriptorPool{ VK_NULL_HANDLE };
XMINT2 windowSize{};
void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags)
{
int index = 0;
while (propertyFlags) {
if (propertyFlags & 1) {
if (index != 0)
printf("|");
printf(string_VkMemoryPropertyFlagBits((VkMemoryPropertyFlagBits)(1u << index)));
}
propertyFlags >>= 1;
index += 1;
};
printf("\n");
}
XMMATRIX currentProjection()
{
float fov_angle_y = XMConvertToRadians(45 * 1.0);
float aspect_ratio = (float)windowSize.x / (float)windowSize.y;
float near_z = 0.1;
float far_z = 1000.0;
XMMATRIX projection = XMMatrixPerspectiveFovLH(fov_angle_y, aspect_ratio, near_z, far_z);
return projection * XMMatrixScaling(-1, -1, 1);
}
/*
XMMATRIX currentView(collada::instance_types::node const & camera_node,
collada::instance_types::node const & camera_target_node)
{
XMVECTOR eye = XMVector3Transform(XMVectorZero(), camera_node.world);
XMVECTOR at = XMVector3Transform(XMVectorZero(), camera_target_node.world);
XMVECTOR up = XMVectorSet(0, 0, 1, 0);
XMMATRIX view = XMMatrixLookAtLH(eye, at, up);
return view;
}
*/
float theta = 0;
XMMATRIX currentModel()
{
theta += 0.01;
return XMMatrixTranslation(0, 0, 0.0) * XMMatrixRotationX(theta) * XMMatrixRotationZ(XM_PI * 0.5f);
}
void createDepth(VkDeviceSize nonCoherentAtomSize,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
uint32_t width,
uint32_t height,
VkFormat format,
VkImageUsageFlags usage,
VkImageAspectFlags aspect,
uint32_t arrayLayers,
VkImage * image,
VkDeviceMemory * memory,
VkImageView * imageView)
{
VkImageCreateInfo imageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent{
.width = width,
.height = height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = arrayLayers,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = usage,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VK_CHECK(vkCreateImage(device, &imageCreateInfo, nullptr, image));
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(device, *image, &memoryRequirements);
VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };
VkMemoryAllocateFlags memoryAllocateFlags{ };
VkDeviceSize stride;
allocateFromMemoryRequirements(device,
nonCoherentAtomSize,
physicalDeviceMemoryProperties,
memoryRequirements,
memoryPropertyFlags,
memoryAllocateFlags,
1,
memory,
&stride);
VK_CHECK(vkBindImageMemory(device, *image, *memory, 0));
VkImageViewType viewType{ (arrayLayers == 1) ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY };
VkImageViewCreateInfo imageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = *image,
.viewType = viewType,
.format = format,
.subresourceRange{
.aspectMask = aspect,
.levelCount = 1,
.layerCount = arrayLayers
}
};
VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, imageView));
}
void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat,
VkFormat depthFormat,
VkDeviceSize nonCoherentAtomSize,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkSurfaceCapabilitiesKHR const & surfaceCapabilities)
{
//////////////////////////////////////////////////////////////////////
// swapchain and images
//////////////////////////////////////////////////////////////////////
VkExtent2D imageExtent {
.width = surfaceCapabilities.currentExtent.width,
.height = surfaceCapabilities.currentExtent.height,
};
VkFormat imageFormat{ surfaceFormat.format };
VkColorSpaceKHR imageColorSpace{ surfaceFormat.colorSpace };
VkSwapchainCreateInfoKHR swapchainCreateInfo{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount = surfaceCapabilities.minImageCount,
.imageFormat = imageFormat,
.imageColorSpace = imageColorSpace,
.imageExtent = imageExtent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR
};
if (swapchain != VK_NULL_HANDLE) {
swapchainCreateInfo.oldSwapchain = swapchain;
}
VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain));
if (swapchainImages != nullptr) {
free(swapchainImages);
}
if (swapchainImageViews != nullptr) {
for (uint32_t i = 0; i < swapchainImageCount; i++) {
vkDestroyImageView(device, swapchainImageViews[i], nullptr);
}
free(swapchainImageViews);
}
if (renderSemaphores != nullptr) {
for (uint32_t i = 0; i < swapchainImageCount; i++) {
vkDestroySemaphore(device, renderSemaphores[i], nullptr);
}
free(renderSemaphores);
}
VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr));
swapchainImages = NewM<VkImage>(swapchainImageCount);
VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages));
swapchainImageViews = NewM<VkImageView>(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; i++) {
VkImageViewCreateInfo imageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = swapchainImages[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = imageFormat,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, &swapchainImageViews[i]));
}
if (swapchainCreateInfo.oldSwapchain != nullptr) {
vkDestroySwapchainKHR(device, swapchainCreateInfo.oldSwapchain, nullptr);
}
//////////////////////////////////////////////////////////////////////
// render semaphores
//////////////////////////////////////////////////////////////////////
VkSemaphoreCreateInfo semaphoreCreateInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
};
renderSemaphores = NewM<VkSemaphore>(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; i++) {
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderSemaphores[i]));
}
//////////////////////////////////////////////////////////////////////
// depth
//////////////////////////////////////////////////////////////////////
if (depthImage != VK_NULL_HANDLE) {
vkDestroyImage(device, depthImage, nullptr);
}
if (depthMemory != VK_NULL_HANDLE) {
vkFreeMemory(device, depthMemory, nullptr);
}
if (depthImageView != VK_NULL_HANDLE) {
vkDestroyImageView(device, depthImageView, nullptr);
}
uint32_t arrayLayers{ 1 };
createDepth(nonCoherentAtomSize,
physicalDeviceMemoryProperties,
imageExtent.width,
imageExtent.height,
depthFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
arrayLayers,
&depthImage,
&depthMemory,
&depthImageView);
}
inline static int positive_modulo(int i, unsigned int n) {
return (i % n + n) % n;
}
inline static double getTime(int64_t start_time)
{
int64_t current_time;
SDL_GetCurrentTime(&current_time);
int64_t time = current_time - start_time;
return (double)(time / 1000) * 0.000001;
}
static int const max_gamepads = 16;
static SDL_Gamepad * gamepads[max_gamepads];
static int gamepad_count = 0;
void add_gamepad(SDL_JoystickID instance_id)
{
SDL_Gamepad * gamepad = SDL_OpenGamepad(instance_id);
char const * name = SDL_GetGamepadName(gamepad);
if (gamepad_count >= max_gamepads) {
printf("too many gamepads; ignoring gamepad %d %s\n", instance_id, name);
SDL_CloseGamepad(gamepad);
} else {
printf("add gamepad %d %s\n", instance_id, name);
gamepads[gamepad_count] = gamepad;
gamepad_count += 1;
}
}
void remove_gamepad(SDL_JoystickID instance_id)
{
for (int i = 0; i < gamepad_count; i++) {
if (SDL_GetGamepadID(gamepads[i]) == instance_id) {
int tail = (gamepad_count - i) - 1;
SDL_CloseGamepad(gamepads[i]);
memcpy(&gamepads[i], &gamepads[i+1], tail * (sizeof (gamepads[0])));
gamepad_count -= 1;
return;
}
}
assert(!"remove_gamepad");
}
void gamepad_update(view & viewState)
{
for (int i = 0; i < gamepad_count; i++) {
SDL_Gamepad * gamepad = gamepads[i];
int16_t i_leftx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
int16_t i_lefty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
int16_t i_rightx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX);
int16_t i_righty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY);
int16_t i_left_trigger = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
int16_t i_right_trigger = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
constexpr float scale = 1.0f / 32767.0f;
float leftx = (float)i_leftx * scale;
float lefty = (float)i_lefty * scale;
float rightx = (float)i_rightx * scale;
float righty = (float)i_righty * scale;
float left_trigger = (float)i_left_trigger * scale;
float right_trigger = (float)i_right_trigger * scale;
constexpr float translate_rate = 0.5;
float delta_forward = -lefty * translate_rate;
float delta_strafe = leftx * translate_rate;
float delta_elevation = (left_trigger - right_trigger) * translate_rate;
float delta_yaw = rightx * -0.035;
float delta_pitch = righty * -0.035;
//viewState.applyTransform(delta_forward, delta_strafe, delta_elevation,
//delta_yaw, delta_pitch);
}
}
void offscreenRender(VkCommandBuffer commandBuffer, int frameIndex,
int mx, int my, int colorIndex, bool drawText,
renpy::vulkan const & renpy_state,
renpy::interpreter const & interpreter_state,
font::outline::font const & font_state,
VkSurfaceCapabilitiesKHR const & surfaceCapabilities)
{
// barrier
constexpr int colorBarriersCount = 2;
VkImageMemoryBarrier2 colorBarriers[colorBarriersCount]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = colorImage[colorIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
},
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = depthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = 1
}
}
};
VkDependencyInfo colorBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = colorBarriersCount,
.pImageMemoryBarriers = colorBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &colorBarrierDependencyInfo);
// attachments
VkRenderingAttachmentInfo colorRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = colorImageView[colorIndex],
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue{ .color{ { 0.0f, 0.0f, 0.0f, 0.0f} } }
};
VkRenderingAttachmentInfo depthRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.clearValue{ .depthStencil{ 1.0f, 0 } }
};
VkRenderingInfo colorRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea{ .extent{ .width = 1280, .height = 720 } },
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &colorRenderingAttachmentInfo,
.pDepthAttachment = &depthRenderingAttachmentInfo,
.pStencilAttachment = &depthRenderingAttachmentInfo,
};
vkCmdBeginRendering(commandBuffer, &colorRenderingInfo);
// viewport/scissor
VkViewport viewport{
.x = 0,
.y = 0,
.width = 1280,
.height = 720,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{
.extent{
.width = 1280,
.height = 720
}
};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// draw
//collada_state.vulkan.excludeMaterialIndex = lightMaterialIndex;
//collada_state.vulkan.pipelineIndex = 0; // shadow pipeline
//collada_state.draw();
renpy_state.draw(commandBuffer, frameIndex, interpreter_state,
mx, my, drawText,
surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height);
if (drawText) {
font_state.draw(commandBuffer, frameIndex, interpreter_state);
}
vkCmdEndRendering(commandBuffer);
// barrier
{
VkImageMemoryBarrier2 colorBarriers[1]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.image = colorImage[frameIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1,
}
}
};
VkDependencyInfo colorBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = colorBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &colorBarrierDependencyInfo);
}
}
static inline double clamp01(double a)
{
if (a < 0.0) return 0.0;
if (a > 1.0) return 1.0;
return a;
}
void handlePause(renpy::interpreter & interpreter_state,
int64_t const start_time,
double & pause_start,
double & dissolve_start)
{
if (interpreter_state.pause.pause) {
if (pause_start == 0.0) {
fprintf(stderr, "pause %f\n", interpreter_state.pauseDuration);
pause_start = getTime(start_time);
} else if (getTime(start_time) - pause_start >= interpreter_state.pauseDuration) {
fprintf(stderr, "unpause %f\n", interpreter_state.pauseDuration);
pause_start = 0.0;
interpreter_state.pause.pause = false;
}
}
if (interpreter_state.pause.dissolve) {
if (dissolve_start == 0.0) {
fprintf(stderr, "dissolve %f\n", interpreter_state.dissolveDuration);
dissolve_start = getTime(start_time);
} else if (getTime(start_time) - dissolve_start >= interpreter_state.dissolveDuration) {
fprintf(stderr, "undissolve %f\n", interpreter_state.dissolveDuration);
dissolve_start = 0.0;
interpreter_state.pause.dissolve = false;
}
}
if (interpreter_state.pause.voice) {
if (!audio::exists(interpreter_state.voiceAudioIndex)) {
fprintf(stderr, "voice unpause %d\n", interpreter_state.voiceAudioIndex);
interpreter_state.pause.voice = false;
interpreter_state.highlightImages(nullptr, 0);
}
}
}
int main()
{
file::init();
SDL_InitFlags init_flags = SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO;
SDL_CHECK(SDL_Init(init_flags));
SDL_CHECK(SDL_Vulkan_LoadLibrary(NULL));
#ifndef __APPLE__
volkInitialize();
#endif
VkApplicationInfo appInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Vulkan",
.apiVersion = VK_API_VERSION_1_3
};
uint32_t instanceExtensionsCount{ 0 };
char const * const * instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) };
VkInstanceCreateInfo instanceCreateInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledExtensionCount = instanceExtensionsCount,
.ppEnabledExtensionNames = instanceExtensions,
};
VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
#ifndef __APPLE__
volkLoadInstance(instance);
#endif
//////////////////////////////////////////////////////////////////////
// physical device and queue family index
//////////////////////////////////////////////////////////////////////
uint32_t physicalDeviceCount{ 0 };
VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr));
VkPhysicalDevice * physicalDevices = NewM<VkPhysicalDevice>(physicalDeviceCount);
VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices));
uint32_t physicalDeviceIndex{ 0 };
printf("physicalDeviceCount %d\n", physicalDeviceCount);
VkPhysicalDeviceProperties physicalDeviceProperties;
for (uint32_t i = 0; i < physicalDeviceCount; i++) {
VkPhysicalDeviceProperties2 properties{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
vkGetPhysicalDeviceProperties2(physicalDevices[i], &properties);
printf("devices[%d] name: %s%s\n", i, properties.properties.deviceName, (i == physicalDeviceIndex) ? " [selected]" : "");
if (i == physicalDeviceIndex) {
printf("limits:\n");
printf(" maxImageDimension1D %u\n", properties.properties.limits.maxImageDimension1D);
printf(" maxImageDimension2D %u\n", properties.properties.limits.maxImageDimension2D);
printf(" maxMemoryAllocationCount %u\n", properties.properties.limits.maxMemoryAllocationCount);
printf(" maxSamplerAllocationCount %u\n", properties.properties.limits.maxSamplerAllocationCount);
printf(" nonCoherentAtomSize %lu\n", properties.properties.limits.nonCoherentAtomSize);
printf(" minUniformBufferOffsetAlignment %lu\n", properties.properties.limits.minUniformBufferOffsetAlignment);
printf(" maxSamplerLodBias %f\n", properties.properties.limits.maxSamplerLodBias);
printf(" maxSamplerAnisotropy %f\n", properties.properties.limits.maxSamplerAnisotropy);
physicalDeviceProperties = properties.properties;
}
}
VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
free(physicalDevices);
uint32_t queueFamilyCount{ 0 };
vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, nullptr);
VkQueueFamilyProperties2 * queueFamilyProperties = NewM<VkQueueFamilyProperties2>(queueFamilyCount);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
queueFamilyProperties[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
queueFamilyProperties[i].pNext = nullptr;
}
vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, queueFamilyProperties);
uint32_t queueFamilyIndex{ ~0u };
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkQueueFlags queueFlags = queueFamilyProperties[i].queueFamilyProperties.queueFlags;
if ((queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFlags & VK_QUEUE_COMPUTE_BIT)) {
queueFamilyIndex = i;
break;
}
}
ASSERT(queueFamilyIndex != ~0u, "no queue with VK_QUEUE_GRAPHICS_BIT && VK_QUEUE_COMPUTE_BIT");
free(queueFamilyProperties);
SDL_CHECK(SDL_Vulkan_GetPresentationSupport(instance, physicalDevice, queueFamilyIndex));
//////////////////////////////////////////////////////////////////////
// memory
//////////////////////////////////////////////////////////////////////
VkPhysicalDeviceMemoryProperties2 physicalDeviceMemoryProperties2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
.pNext = nullptr
};
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &physicalDeviceMemoryProperties2);
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties = physicalDeviceMemoryProperties2.memoryProperties;
if constexpr (true) {
for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) {
printf("memoryTypes[%u].propertyFlags: ", i);
print_memoryPropertyFlags(physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags);
printf("memoryTypes[%u].heapIndex: %u\n", i, physicalDeviceMemoryProperties.memoryTypes[i].heapIndex);
}
for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryHeapCount; i++) {
printf("memoryHeaps[%u].size %lu\n", i, physicalDeviceMemoryProperties.memoryHeaps[i].size);
printf("memoryHeaps[%u].flags %08x\n", i, physicalDeviceMemoryProperties.memoryHeaps[i].flags);
}
}
//////////////////////////////////////////////////////////////////////
// device and queue
//////////////////////////////////////////////////////////////////////
const float queuePriorities{ 1.0f };
VkDeviceQueueCreateInfo queueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queuePriorities
};
VkPhysicalDeviceVulkan12Features enabledVulkan12Features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
};
VkPhysicalDeviceVulkan13Features enabledVulkan13Features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &enabledVulkan12Features,
.synchronization2 = true,
.dynamicRendering = true,
};
VkPhysicalDeviceFeatures enabledFeatures{
//.geometryShader = true,
.samplerAnisotropy = true,
};
constexpr uint32_t enabledExtensionCount = 1;
char const * enabledExtensionNames[enabledExtensionCount]{ VK_KHR_SWAPCHAIN_EXTENSION_NAME };
VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &enabledVulkan13Features,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensionNames,
.pEnabledFeatures = &enabledFeatures
};
VK_CHECK(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device));
vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue);
//////////////////////////////////////////////////////////////////////
// window and surface
//////////////////////////////////////////////////////////////////////
SDL_Window * window = SDL_CreateWindow("The Road to Alysen", 1280, 720, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
SDL_CHECK_NONNULL(window);
SDL_CHECK(SDL_SetWindowMinimumSize(window, 1280, 720));
SDL_CHECK(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface));
SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y));
VkSurfaceCapabilitiesKHR surfaceCapabilities{};
VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities));
if ((surfaceCapabilities.currentExtent.width == ~0u) && (surfaceCapabilities.currentExtent.width == ~0u)) {
surfaceCapabilities.currentExtent.width = windowSize.x;
surfaceCapabilities.currentExtent.height = windowSize.y;
}
printf("surfaceCapabilities currentExtent %d %d\n", surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height);
// surface format
uint32_t surfaceFormatCount{ 0 };
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, nullptr));
VkSurfaceFormatKHR * surfaceFormats = NewM<VkSurfaceFormatKHR>(surfaceFormatCount);
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, surfaceFormats));
uint32_t surfaceFormatIndex{ 0 };
printf("surfaceFormatCount %d\n", surfaceFormatCount);
for (uint32_t i = 0; i < surfaceFormatCount; i++) {
if (surfaceFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM) {
surfaceFormatIndex = i;
break;
}
if (surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
surfaceFormatIndex = i;
break;
}
}
for (uint32_t i = 0; i < surfaceFormatCount; i++) {
printf("surfaceFormat[%d] %s %s%s\n", i, string_VkFormat(surfaceFormats[i].format), string_VkColorSpaceKHR(surfaceFormats[i].colorSpace), (i == surfaceFormatIndex) ? " [selected]" : "");
}
VkSurfaceFormatKHR surfaceFormat = surfaceFormats[surfaceFormatIndex];
free(surfaceFormats);
// depth format
VkFormat depthFormat{ VK_FORMAT_UNDEFINED };
constexpr uint32_t depthFormatCount = 2;
VkFormat depthFormatList[depthFormatCount]{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT };
for (uint32_t i = 0; i < depthFormatCount; i++) {
VkFormatProperties2 formatProperties{ .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 };
vkGetPhysicalDeviceFormatProperties2(physicalDevice, depthFormatList[i], &formatProperties);
if (formatProperties.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
depthFormat = depthFormatList[i];
break;
}
}
ASSERT(depthFormat != VK_FORMAT_UNDEFINED, "no depth format with VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT");
printf("depthFormat: %s\n", string_VkFormat(depthFormat));
recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceProperties.limits.nonCoherentAtomSize, physicalDeviceMemoryProperties, surfaceCapabilities);
//////////////////////////////////////////////////////////////////////
// shadow
//////////////////////////////////////////////////////////////////////
createDepth(physicalDeviceProperties.limits.nonCoherentAtomSize,
physicalDeviceMemoryProperties,
1280,
720,
depthFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
shadowArrayLayers,
&shadowDepthImage,
&shadowDepthMemory,
&shadowDepthImageView);
VkImageViewCreateInfo imageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = shadowDepthImage,
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.format = depthFormat,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.levelCount = 1,
.layerCount = shadowArrayLayers
}
};
VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, &shadowDepthImageViewDepth));
//////////////////////////////////////////////////////////////////////
// synchronization objects
//////////////////////////////////////////////////////////////////////
VkSemaphoreCreateInfo semaphoreCreateInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
};
VkFenceCreateInfo fenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT
};
for (uint32_t i = 0; i < maxFramesInFlight; i++) {
VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fences[i]));
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &presentSemaphores[i]));
}
//////////////////////////////////////////////////////////////////////
// command
//////////////////////////////////////////////////////////////////////
VkCommandPoolCreateInfo commandPoolCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queueFamilyIndex
};
VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool));
VkCommandBufferAllocateInfo commandBufferAllocateCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = commandPool,
.commandBufferCount = maxFramesInFlight
};
VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateCreateInfo, commandBuffers));
//////////////////////////////////////////////////////////////////////
// texture sampler
//////////////////////////////////////////////////////////////////////
VkSamplerCreateInfo samplerCreateInfo0{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.anisotropyEnable = VK_FALSE,
.maxLod = 0.0, // (float)ddsFile->header.dwMipMapCount
};
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo0, nullptr, &textureSamplers[0]));
VkSamplerCreateInfo samplerCreateInfo1{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.anisotropyEnable = VK_FALSE,
.maxLod = VK_LOD_CLAMP_NONE, // (float)ddsFile->header.dwMipMapCount,
};
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo1, nullptr, &textureSamplers[1]));
VkSamplerCreateInfo samplerCreateInfo2{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_NEAREST,
.minFilter = VK_FILTER_NEAREST,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 16.0f,
.maxLod = 3.0, // (float)ddsFile->header.dwMipMapCount,
};
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo2, nullptr, &textureSamplers[2]));
//////////////////////////////////////////////////////////////////////
// off-screen color buffers
//////////////////////////////////////////////////////////////////////
for (int i = 0; i < colorImageCount; i++) {
uint32_t arrayLayers{ 1 };
createDepth(physicalDeviceProperties.limits.nonCoherentAtomSize,
physicalDeviceMemoryProperties,
1280,
720,
surfaceFormat.format,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT,
arrayLayers,
&colorImage[i],
&colorMemory[i],
&colorImageView[i]);
}
//////////////////////////////////////////////////////////////////////
// initialize collada
//////////////////////////////////////////////////////////////////////
/*
collada::scene::state collada_state;
collada_state.vulkan.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[0],
shadowDepthImageViewDepth);
collada::types::descriptor const * collada_scene_descriptor = &eidelwind::descriptor;
collada_state.load_scene(collada_scene_descriptor);
*/
//////////////////////////////////////////////////////////////////////
// initialize minecraft
//////////////////////////////////////////////////////////////////////
/*
minecraft::vulkan::vulkan minecraft_state;
minecraft_state.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[2],
shadowDepthImageViewDepth);
minecraft_state.init();
*/
//////////////////////////////////////////////////////////////////////
// initialize font
//////////////////////////////////////////////////////////////////////
font::outline::font font_state;
font_state.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[2]);
font_state.init();
//////////////////////////////////////////////////////////////////////
// initialize renpy
//////////////////////////////////////////////////////////////////////
renpy::vulkan renpy_state;
renpy_state.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[2]);
renpy_state.init();
//////////////////////////////////////////////////////////////////////
// interpreter
//////////////////////////////////////////////////////////////////////
renpy::interpreter interpreter_state;
interpreter_state.reset(12);
//////////////////////////////////////////////////////////////////////
// renpy composite
//////////////////////////////////////////////////////////////////////
renpy::composite::vulkan composite_state;
composite_state.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[2]);
composite_state.init();
composite_state.write_descriptor_sets(colorImageView, colorImageCount);
//////////////////////////////////////////////////////////////////////
// initialize view
//////////////////////////////////////////////////////////////////////
/*
int cameraIndex = collada_state.find_node_index_by_name("Camera001");
int cameraTargetIndex = collada_state.find_node_index_by_name("EidelwindRigPelvis");
int lightIndex = collada_state.find_node_index_by_name("Camera001");
int lightTargetIndex = collada_state.find_node_index_by_name("EidelwindRigPelvis");
//int lightMaterialIndex = collada_state.find_material_index_by_name("LightMaterial");
int lightMaterialIndex = -1;
// view
XMVECTOR eye = XMVector3Transform(XMVectorZero(), collada_state.node_state.node_instances[cameraIndex].world);
XMVECTOR at = XMVector3Transform(XMVectorZero(), collada_state.node_state.node_instances[cameraTargetIndex].world);
XMVECTOR up = XMVectorSet(0, 0, 1, 0);
view viewState;
viewState.eye = eye;
//viewState.at = at;
viewState.up = up;
viewState.forward = XMVectorSetZ(XMVector3Normalize(at - eye), 0);
viewState.pitch = 0;
viewState.applyTransform(0, 0, 0, 0, 0);
*/
//////////////////////////////////////////////////////////////////////
// loop
//////////////////////////////////////////////////////////////////////
bool updateSwapchain{ false };
uint32_t frameIndex{ 0 };
uint32_t imageIndex{ 0 };
bool quit{ false };
int32_t samplerIndex{ 0 };
int64_t start_time;
SDL_GetCurrentTime(&start_time);
double pause_start = 0.0;
double dissolve_start = 0.0;
//collada_state.update(0);
audio::init();
audio::load(renpy::script::audio, renpy::script::audio_length);
bool useGamepad = false;
uint32_t whichGamepad = 0;
while (quit == false) {
audio::update();
//////////////////////////////////////////////////////////////////////
// interpreter update
//////////////////////////////////////////////////////////////////////
handlePause(interpreter_state, start_time, pause_start, dissolve_start);
interpreter_state.interpret();
handlePause(interpreter_state, start_time, pause_start, dissolve_start);
//////////////////////////////////////////////////////////////////////
// sdl
//////////////////////////////////////////////////////////////////////
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
quit = true;
}
if (event.type == SDL_EVENT_KEY_DOWN) {
if (event.key.key == SDLK_ESCAPE) {
quit = true;
}
if (!event.key.repeat) {
switch (event.key.key) {
case SDLK_LEFT:
samplerIndex = positive_modulo(samplerIndex - 1, 3);
printf("samplerIndex: %d\n", samplerIndex);
break;
case SDLK_RIGHT:
samplerIndex = positive_modulo(samplerIndex + 1, 3);
printf("samplerIndex: %d\n", samplerIndex);
break;
default:
break;
}
}
}
if (event.type == SDL_EVENT_MOUSE_MOTION) {
useGamepad = false;
if (event.motion.state & SDL_BUTTON_LMASK) {
//collada_state.mouse_motion(cameraIndex, cameraTargetIndex, event.motion.xrel, event.motion.yrel, 0);
}
if (event.motion.state & SDL_BUTTON_RMASK) {
//collada_state.mouse_motion(cameraIndex, cameraTargetIndex, event.motion.xrel, event.motion.yrel, 1);
}
}
if (event.type == SDL_EVENT_WINDOW_RESIZED) {
SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y));
}
if (event.type == SDL_EVENT_GAMEPAD_ADDED) {
add_gamepad(event.gdevice.which);
}
if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
remove_gamepad(event.gdevice.which);
}
if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
whichGamepad = event.gbutton.which;
useGamepad = true;
fprintf(stderr, "use gamepad: which %d\n", whichGamepad);
}
}
//////////////////////////////////////////////////////////////////////
// renpy update
//////////////////////////////////////////////////////////////////////
float mx;
float my;
uint32_t mouseFlags = SDL_GetMouseState(&mx, &my);
bool mLeft = (mouseFlags & SDL_BUTTON_LMASK) != 0;
bool gUp = false;
bool gDown = false;
bool gAccept = false;
if (useGamepad) {
for (int i = 0; i < gamepad_count; i++) {
if (SDL_GetGamepadID(gamepads[i]) == whichGamepad) {
gUp = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_DPAD_UP);
gDown = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_DPAD_DOWN);
gAccept = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_SOUTH) || SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_EAST);
break;
}
}
}
renpy::update(interpreter_state, mx, my, mLeft,
gUp, gDown, gAccept, useGamepad,
surfaceCapabilities.currentExtent.width,
surfaceCapabilities.currentExtent.height);
//////////////////////////////////////////////////////////////////////
// gamepad update
//////////////////////////////////////////////////////////////////////
//gamepad_update(viewState);
//////////////////////////////////////////////////////////////////////
// collada update
//////////////////////////////////////////////////////////////////////
//double time = getTime(start_time);
//collada_state.update(time / 1.5f);
//////////////////////////////////////////////////////////////////////
// fence
//////////////////////////////////////////////////////////////////////
// wait for fence
VK_CHECK(vkWaitForFences(device, 1, &fences[frameIndex], true, UINT64_MAX));
VK_CHECK(vkResetFences(device, 1, &fences[frameIndex]));
// acquire next image
VK_CHECK_SWAPCHAIN(vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, presentSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex));
// command buffer
VkCommandBuffer& commandBuffer = commandBuffers[frameIndex];
VK_CHECK(vkResetCommandBuffer(commandBuffer, 0));
VkCommandBufferBeginInfo commandBufferBeginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
};
VK_CHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo));
//////////////////////////////////////////////////////////////////////
// transfer
//////////////////////////////////////////////////////////////////////
if (0) {
/*
collada_state.vulkan.change_frame(commandBuffer, frameIndex);
XMMATRIX projection = currentProjection();
//XMMATRIX view = currentView(collada_state.node_state.node_instances[cameraIndex],
//collada_state.node_state.node_instances[cameraTargetIndex]);
XMMATRIX view = viewState.getView();
XMMATRIX shadowProjection = XMMatrixOrthographicLH(150, 150, -1000, 1000);
XMMATRIX shadowView = currentView(collada_state.node_state.node_instances[lightIndex],
collada_state.node_state.node_instances[lightTargetIndex]);
collada::instance_types::node const & lightNode = collada_state.node_state.node_instances[lightIndex];
XMVECTOR lightPositionWorld = XMVector3Transform(XMVectorZero(), lightNode.world);
collada_state.vulkan.transfer_transforms(projection,
view,
shadowProjection,
shadowView,
lightPositionWorld,
collada_state.descriptor->nodes_count,
collada_state.node_state.node_instances);
*/
// minecraft
/*
minecraft_state.transfer_transforms(projection,
view,
lightPositionWorld,
frameIndex);
*/
}
//////////////////////////////////////////////////////////////////////
// shadow render
//////////////////////////////////////////////////////////////////////
/*
// barrier
VkImageMemoryBarrier2 shadowBarriers[1]{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = shadowDepthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = shadowArrayLayers
}
}
};
VkDependencyInfo shadowBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = shadowBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &shadowBarrierDependencyInfo);
// attachments
VkRenderingAttachmentInfo shadowDepthRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = shadowDepthImageView,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue{ .depthStencil{ 1.0f, 0 } }
};
VkRenderingInfo shadowRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea{ .extent{ .width = 1280, .height = 720 } },
.layerCount = 6,
.colorAttachmentCount = 0,
.pDepthAttachment = &shadowDepthRenderingAttachmentInfo,
.pStencilAttachment = &shadowDepthRenderingAttachmentInfo,
};
vkCmdBeginRendering(commandBuffer, &shadowRenderingInfo);
// viewport/scissor
VkViewport shadowViewport{
.x = 0,
.y = 0,
.width = 1280,
.height = 720,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &shadowViewport);
VkRect2D shadowScissor{
.extent{
.width = 1280,
.height = 720
}
};
vkCmdSetScissor(commandBuffer, 0, 1, &shadowScissor);
// draw
//collada_state.vulkan.excludeMaterialIndex = lightMaterialIndex;
//collada_state.vulkan.pipelineIndex = 0; // shadow pipeline
//collada_state.draw();
vkCmdEndRendering(commandBuffer);
// barrier
{
VkImageMemoryBarrier2 shadowBarriers[1]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
.image = shadowDepthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = shadowArrayLayers
}
}
};
VkDependencyInfo shadowBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = shadowBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &shadowBarrierDependencyInfo);
}
*/
//////////////////////////////////////////////////////////////////////
// offscreen render
//////////////////////////////////////////////////////////////////////
if (interpreter_state.pause.dissolve) {
offscreenRender(commandBuffer, frameIndex, mx, my, 2, true, renpy_state, interpreter_state, font_state, surfaceCapabilities);
} else {
offscreenRender(commandBuffer, frameIndex, mx, my, 0, true, renpy_state, interpreter_state, font_state, surfaceCapabilities);
offscreenRender(commandBuffer, frameIndex, mx, my, 1, false, renpy_state, interpreter_state, font_state, surfaceCapabilities);
}
//////////////////////////////////////////////////////////////////////
// render
//////////////////////////////////////////////////////////////////////
VkImageMemoryBarrier2 outputBarriers[2]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = swapchainImages[imageIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
},
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = depthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = 1
}
}
};
VkDependencyInfo barrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 2,
.pImageMemoryBarriers = outputBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &barrierDependencyInfo);
// attachments
VkRenderingAttachmentInfo colorRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = swapchainImageViews[imageIndex],
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
//.clearValue{ .color{ 0.0f, 0.0f, 0.2f, 1.0f } }
.clearValue{ .color{ { 0.0f, 0.0f, 0.0f, 0.0f} } }
};
VkRenderingAttachmentInfo depthRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.clearValue{ .depthStencil{ 1.0f, 0 } }
};
VkRenderingInfo renderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea{ .extent{ .width = surfaceCapabilities.currentExtent.width, .height = surfaceCapabilities.currentExtent.height } },
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &colorRenderingAttachmentInfo,
.pDepthAttachment = &depthRenderingAttachmentInfo,
.pStencilAttachment = &depthRenderingAttachmentInfo,
};
vkCmdBeginRendering(commandBuffer, &renderingInfo);
VkViewport viewport{
.x = 0,
.y = 0,
.width = static_cast<float>(surfaceCapabilities.currentExtent.width),
.height = static_cast<float>(surfaceCapabilities.currentExtent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{
.extent{
.width = (uint32_t)surfaceCapabilities.currentExtent.width,
.height = (uint32_t)surfaceCapabilities.currentExtent.height
}
};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// draw
//collada_state.vulkan.excludeMaterialIndex = -1;
//collada_state.vulkan.pipelineIndex = 1; // non-shadow pipeline
//collada_state.draw();
//collada_state.vulkan.pipelineIndex = 2; // geometry shader pipeline
//collada_state.draw();
//minecraft_state.draw(commandBuffer, frameIndex);
//renpy_state.draw(commandBuffer, frameIndex, interpreter_state, mx, my);
//font_state.draw(commandBuffer, frameIndex, interpreter_state);
float dissolve_lerp = -1.0;
float text_lerp = -1.0;
constexpr double textDissolveDuration = 0.3;
if (interpreter_state.pause.dissolve) {
double current_time = getTime(start_time);
if (current_time < dissolve_start)
current_time = dissolve_start;
double delta = current_time - dissolve_start;
assert(interpreter_state.dissolveDuration > 0.0000001);
dissolve_lerp = clamp01(delta / interpreter_state.dissolveDuration);
text_lerp = clamp01(delta / textDissolveDuration);
}
composite_state.draw(commandBuffer, frameIndex, dissolve_lerp, text_lerp, surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height);
vkCmdEndRendering(commandBuffer);
// barrier
VkImageMemoryBarrier2 presentBarriers[1]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = swapchainImages[imageIndex],
.subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 }
},
/*
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.image = shadowDepthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = 1
}
}
*/
};
VkDependencyInfo barrierPresentDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = presentBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &barrierPresentDependencyInfo);
VK_CHECK(vkEndCommandBuffer(commandBuffer));
//////////////////////////////////////////////////////////////////////
// submit to graphics queue
//////////////////////////////////////////////////////////////////////
VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &presentSemaphores[frameIndex],
.pWaitDstStageMask = &waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &renderSemaphores[imageIndex],
};
VK_CHECK(vkQueueSubmit(queue, 1, &submitInfo, fences[frameIndex]));
frameIndex = (frameIndex + 1) % maxFramesInFlight;
// present
VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &renderSemaphores[imageIndex],
.swapchainCount = 1,
.pSwapchains = &swapchain,
.pImageIndices = &imageIndex
};
VK_CHECK_SWAPCHAIN(vkQueuePresentKHR(queue, &presentInfo));
if (updateSwapchain) {
//////////////////////////////////////////////////////////////////////
// recreate swapchain
//////////////////////////////////////////////////////////////////////
updateSwapchain = false;
VK_CHECK(vkDeviceWaitIdle(device));
VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities));
if ((surfaceCapabilities.currentExtent.width == ~0u) && (surfaceCapabilities.currentExtent.width == ~0u)) {
surfaceCapabilities.currentExtent.width = windowSize.x;
surfaceCapabilities.currentExtent.height = windowSize.y;
}
recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceProperties.limits.nonCoherentAtomSize, physicalDeviceMemoryProperties, surfaceCapabilities);
}
}
VK_CHECK(vkDeviceWaitIdle(device));
//collada_state.vulkan.destroy_all(collada_scene_descriptor);
//collada_state.unload_scene();
for (uint32_t i = 0; i < maxFramesInFlight; i++) {
vkDestroyFence(device, fences[i], nullptr);
vkDestroySemaphore(device, presentSemaphores[i], nullptr);
}
for (uint32_t i = 0; i < swapchainImageCount; i++) {
vkDestroySemaphore(device, renderSemaphores[i], nullptr);
vkDestroyImageView(device, swapchainImageViews[i], nullptr);
}
vkDestroyImage(device, depthImage, nullptr);
vkFreeMemory(device, depthMemory, nullptr);
vkDestroyImageView(device, depthImageView, nullptr);
vkDestroyImage(device, shadowDepthImage, nullptr);
vkFreeMemory(device, shadowDepthMemory, nullptr);
vkDestroyImageView(device, shadowDepthImageView, nullptr);
vkDestroyImageView(device, shadowDepthImageViewDepth, nullptr);
vkDestroySampler(device, textureSamplers[0], nullptr);
vkDestroySampler(device, textureSamplers[1], nullptr);
vkDestroySampler(device, textureSamplers[2], nullptr);
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
vkDestroySwapchainKHR(device, swapchain, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyCommandPool(device, commandPool, nullptr);
SDL_DestroyWindow(window);
SDL_QuitSubSystem(init_flags);
SDL_Quit();
vkDestroyDevice(device, nullptr);
vkDestroyInstance(instance, nullptr);
}