We be kinda rendering

This commit is contained in:
2026-01-05 06:20:49 +01:00
parent 1168f9e5d1
commit c83c423b42
48 changed files with 2789 additions and 382 deletions

View File

@@ -6,6 +6,9 @@
#include "spdlog/spdlog.h"
App::App(): renderer{meshCache} {
}
void App::init(const AppParams& params) {
m_params = params;
@@ -30,11 +33,22 @@ void App::init(const AppParams& params) {
}
gfxDevice.init(window, params.appName, false);
renderer.init(gfxDevice, params.renderSize);
//Read whole file
auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt");
std::string fileStr(file.begin(), file.end());
spdlog::info("Read from assetfstest.txt: {}", fileStr);
testMesh.name = "Test Mesh";
testMesh.vertices = vertices;
testMesh.indices = indices;
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
float aspectRatio = static_cast<float>(params.renderSize.x) / static_cast<float>(params.renderSize.y);
camera.setAspectRatio(aspectRatio);
}
void App::run() {
@@ -97,8 +111,16 @@ void App::run() {
}
if (!gfxDevice.needsSwapchainRecreate()) {
renderer.beginDrawing(gfxDevice);
renderer.drawMesh(testMeshID, glm::mat4(1.f), 0);
renderer.endDrawing();
auto cmd = gfxDevice.beginFrame();
gfxDevice.endFrame(cmd, VImage{}, {});
renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{
camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f
});
gfxDevice.endFrame(cmd, GPUImage{}, {});
}
if (frameLimit) {
// Delay to not overload the CPU

View File

@@ -0,0 +1,195 @@
#include <destrum/Graphics/Camera.h>
#include <iostream>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
Camera::Camera(const glm::vec3& position, const glm::vec3& up): m_position{position}, m_up{up} {
}
void Camera::Update(float deltaTime) {
// const auto MyWindow = static_cast<Window*>(glfwGetWindowUserPointer(Window::gWindow));
//
// #pragma region Keyboard Movement
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
//
// float MovementSpeed = m_movementSpeed;
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) {
// MovementSpeed *= 2.0f;
// }
//
// if (glfwGetKey(Window::gWindow, GLFW_KEY_W) == GLFW_PRESS) {
// m_position += m_forward * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_A) == GLFW_PRESS) {
// m_position -= m_right * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_S) == GLFW_PRESS) {
// m_position -= m_forward * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_D) == GLFW_PRESS) {
// m_position += m_right * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_E) == GLFW_PRESS) {
// m_position += m_up * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_Q) == GLFW_PRESS) {
// m_position -= m_up * deltaTime * MovementSpeed;
// }
//
// // Looking around with arrow keys
// constexpr float lookSpeed = 1.0f * glm::radians(1.f);
// if (glfwGetKey(Window::gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
// totalPitch += lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
// totalPitch -= lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
// totalYaw -= lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
// totalYaw += lookSpeed;
// }
//
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>(), glm::half_pi<float>());
//
// const glm::mat4 yawMatrix = glm::rotate(glm::mat4(1.0f), -totalYaw, glm::vec3(0, 1, 0));
// const glm::mat4 pitchMatrix = glm::rotate(glm::mat4(1.0f), totalPitch, glm::vec3(0, 0, 1));
//
// const glm::mat4 rotationMatrix = yawMatrix * pitchMatrix;
//
// m_forward = glm::vec3(rotationMatrix * glm::vec4(1, 0, 0, 0));
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
// m_up = glm::normalize(glm::cross(m_right, m_forward));
// #pragma endregion
//
// #pragma region Mouse Looking
// static bool wasCursorLockedLastFrame = false;
// static double lastMouseX = 0.0;
// static double lastMouseY = 0.0;
// static bool firstMouse = true;
//
// bool isLocked = MyWindow->isCursorLocked();
// if (isLocked) {
// double mouseX, mouseY;
// glfwGetCursorPos(Window::gWindow, &mouseX, &mouseY);
//
// // Reset mouse reference when locking the cursor (prevents jump)
// if (!wasCursorLockedLastFrame) {
// lastMouseX = mouseX;
// lastMouseY = mouseY;
// firstMouse = false;
// }
//
// float xOffset = static_cast<float>(mouseX - lastMouseX);
// float yOffset = static_cast<float>(lastMouseY - mouseY); // Reversed: y goes up
//
// lastMouseX = mouseX;
// lastMouseY = mouseY;
//
// constexpr float sensitivity = 0.002f;
// xOffset *= sensitivity;
// yOffset *= sensitivity;
//
// totalYaw += xOffset;
// totalPitch += yOffset;
//
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
//
// m_frustum.update(m_projectionMatrix * m_viewMatrix);
// }
//
// wasCursorLockedLastFrame = isLocked;
// #pragma endregion
//
// #pragma region Controller Movement
//
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1)) {
// GLFWgamepadstate state;
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1) && glfwGetGamepadState(GLFW_JOYSTICK_1, &state)) {
// float moveSpeed = MovementSpeed * deltaTime;
// const float rotSpeed = 1.5f * deltaTime;
//
//
// //TODO: god knows what this is
// auto deadzone = [] (float value, float threshold = 0.1f) {
// if (fabs(value) < threshold)
// return 0.0f;
// const float sign = (value > 0) ? 1.0f : -1.0f;
// return sign * (fabs(value) - threshold) / (1.0f - threshold);
// };
//
// //LT is x2 speed
// const bool isLTPressed = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
// if (isLTPressed) {
// moveSpeed *= 3.0f;
// }
//
// const float lx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
// const float ly = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
// const float rx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
// const float ry = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
//
// m_position += m_forward * (-ly * moveSpeed);
// m_position += m_right * (lx * moveSpeed);
//
// const float lTrigger = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) / 2.0f;
// const float rTrigger = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) / 2.0f;
// const float vertical = (rTrigger - lTrigger);
// m_position += m_up * vertical * moveSpeed;
//
// totalYaw += rx * rotSpeed;
// totalPitch -= ry * rotSpeed;
// }
// }
// #pragma endregion
CalculateProjectionMatrix();
CalculateViewMatrix();
}
void Camera::CalculateViewMatrix() {
if (m_useTarget) {
m_forward = glm::normalize(m_target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
m_viewMatrix = glm::lookAt(m_position, m_target, m_up);
} else {
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
}
m_invMatrix = glm::inverse(m_viewMatrix);
}
void Camera::CalculateProjectionMatrix() {
m_projectionMatrix = glm::perspectiveRH_ZO(glm::radians(fovAngle), m_aspectRatio, m_zNear, m_zFar);
}
void Camera::ClearTarget() {
m_useTarget = false;
m_forward = glm::normalize(m_target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
}
void Camera::SetTarget(const glm::vec3& target) {
m_target = target;
m_useTarget = true;
// m_forward = glm::normalize(m_target - m_position);
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
// m_up = glm::normalize(glm::cross(m_right, m_forward));
}
void Camera::Target(const glm::vec3& target) {
glm::vec3 directionToTarget = glm::normalize(target - m_position);
m_forward = glm::normalize(target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
m_invMatrix = glm::inverse(m_viewMatrix);
}

View File

@@ -0,0 +1,565 @@
#include <destrum/Graphics/GfxDevice.h>
#include "destrum/Graphics/Util.h"
#define VOLK_IMPLEMENTATION
#include <volk.h>
#define VMA_IMPLEMENTATION
#include <filesystem>
#include <vk_mem_alloc.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <destrum/Graphics/Init.h>
#include "destrum/Graphics/imageLoader.h"
#include "spdlog/spdlog.h"
GfxDevice::GfxDevice(): imageCache(*this) {
}
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
VK_CHECK(volkInitialize());
instance = vkb::InstanceBuilder{}
.set_app_name(appName.c_str())
.set_app_version(1, 0, 0)
.request_validation_layers()
.use_default_debug_messenger()
.require_api_version(1, 3, 0)
.build()
.value();
volkLoadInstance(instance);
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
if (res != SDL_TRUE) {
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
std::exit(1);
}
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
.imageCubeArray = VK_TRUE,
.geometryShader = VK_TRUE, // for im3d
.depthClamp = VK_TRUE,
.samplerAnisotropy = VK_TRUE,
};
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
.descriptorIndexing = true,
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
.scalarBlockLayout = true,
.bufferDeviceAddress = true,
};
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
.synchronization2 = true,
.dynamicRendering = true,
};
physicalDevice = vkb::PhysicalDeviceSelector{instance}
.set_minimum_version(1, 3)
.set_required_features(deviceFeatures)
.set_required_features_12(features12)
.set_required_features_13(features13)
.set_surface(surface)
.select()
.value();
device = vkb::DeviceBuilder{physicalDevice}.build().value();
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
//Vma
const auto vulkanFunctions = VmaVulkanFunctions{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
const auto allocatorInfo = VmaAllocatorCreateInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = physicalDevice,
.device = device,
.pVulkanFunctions = &vulkanFunctions,
.instance = instance,
};
vmaCreateAllocator(&allocatorInfo, &allocator);
executor.init(device, graphicsQueueFamily, graphicsQueue);
int w, h;
SDL_GetWindowSize(window, &w, &h);
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
swapchain.initSync(device);
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
auto& commandPool = frames[i].commandPool;
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
auto& mainCommandBuffer = frames[i].commandBuffer;
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
}
//
// { // create white texture
// std::uint32_t pixel = 0xFFFFFFFF;
// whiteImageId = createImage(
// {
// .format = VK_FORMAT_R8G8B8A8_UNORM,
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
// .extent = VkExtent3D{1, 1, 1},
// },
// "white texture",
// &pixel);
// }
}
void GfxDevice::recreateSwapchain(int width, int height) {
assert(width != 0 && height != 0);
waitIdle();
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
}
VkCommandBuffer GfxDevice::beginFrame() {
swapchain.beginFrame(getCurrentFrameIndex());
const auto& frame = getCurrentFrame();
const auto& cmd = frame.commandBuffer;
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
return cmd;
}
void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) {
// get swapchain image
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
if (swapchainImage == VK_NULL_HANDLE) {
spdlog::info("Swapchain is freaky, skipping frame...");
return;
}
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
swapchain.resetFences(getCurrentFrameIndex());
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
{
// clear swapchain image
VkImageSubresourceRange clearRange =
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
}
// if (props.copyImageIntoSwapchain) {
// copy from draw image into swapchain
// vkutil::transitionImage(
// cmd,
// drawImage.getImage(),
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transitionImage(
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
const auto filter = VK_FILTER_LINEAR;
// // if (props.drawImageBlitRect != glm::ivec4{}) {
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// props.drawImageBlitRect.x,
// props.drawImageBlitRect.y,
// props.drawImageBlitRect.z,
// props.drawImageBlitRect.w,
// filter);
// } else {
// // will stretch image to swapchain
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// getSwapchainExtent(),
// filter);
// }
// prepare for present
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VK_CHECK(vkEndCommandBuffer(cmd));
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
frameNumber++;
}
void GfxDevice::cleanup() {
}
void GfxDevice::waitIdle() {
VK_CHECK(vkDeviceWaitIdle(device));
}
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
executor.immediateSubmit(std::move(f));
}
GPUBuffer GfxDevice::createBuffer(
std::size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage) const
{
const auto bufferInfo = VkBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = allocSize,
.usage = usage,
};
const auto allocInfo = VmaAllocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT |
// TODO: allow to set VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT when needed
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = memoryUsage,
};
GPUBuffer buffer{};
VK_CHECK(vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer.buffer, &buffer.allocation, &buffer.info));
if ((usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) != 0) {
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = buffer.buffer,
};
buffer.address = vkGetBufferDeviceAddress(device, &deviceAdressInfo);
}
return buffer;
}
VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const
{
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = buffer.buffer,
};
return vkGetBufferDeviceAddress(device, &deviceAdressInfo);
}
void GfxDevice::destroyBuffer(const GPUBuffer& buffer) const
{
vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation);
}
//
// GPUImage GfxDevice::loadImageFromFileRaw(
// const std::filesystem::path& path,
// VkFormat format,
// VkImageUsageFlags usage,
// bool mipMap) const
// {
// auto data = util::loadImage(path);
// if (!data.pixels) {
// fmt::println("[error] failed to load image from '{}'", path.string());
// return getImage(errorImageId);
// }
//
// auto image = createImageRaw({
// .format = format,
// .usage = usage | //
// VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image
// VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips
// .extent =
// VkExtent3D{
// .width = (std::uint32_t)data.width,
// .height = (std::uint32_t)data.height,
// .depth = 1,
// },
// .mipMap = mipMap,
// });
// uploadImageData(image, data.pixels);
//
// image.debugName = path.string();
// vkutil::addDebugLabel(device, image.image, path.string().c_str());
//
// return image;
// }
ImageID GfxDevice::createImage(
const vkutil::CreateImageInfo& createInfo,
const std::string& debugName,
void* pixelData,
ImageID imageId)
{
auto image = createImageRaw(createInfo);
if (!debugName.empty()) {
vkutil::addDebugLabel(device, image.image, debugName.c_str());
image.debugName = debugName;
}
if (pixelData) {
uploadImageData(image, pixelData);
}
if (imageId != NULL_IMAGE_ID) {
return imageCache.addImage(imageId, std::move(image));
} else {
return addImageToCache(std::move(image));
}
}
ImageID GfxDevice::createDrawImage(
VkFormat format,
glm::ivec2 size,
const std::string& debugName,
ImageID imageId)
{
assert(size.x > 0 && size.y > 0);
const auto extent = VkExtent3D{
.width = (std::uint32_t)size.x,
.height = (std::uint32_t)size.y,
.depth = 1,
};
VkImageUsageFlags usages{};
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
const auto createImageInfo = vkutil::CreateImageInfo{
.format = format,
.usage = usages,
.extent = extent,
};
return createImage(createImageInfo, debugName, nullptr, imageId);
}
ImageID GfxDevice::loadImageFromFile(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap)
{
return imageCache.loadImageFromFile(path, format, usage, mipMap);
}
const GPUImage& GfxDevice::getImage(ImageID id) const
{
return imageCache.getImage(id);
}
ImageID GfxDevice::addImageToCache(GPUImage img)
{
return imageCache.addImage(std::move(img));
}
GPUImage GfxDevice::createImageRaw(
const vkutil::CreateImageInfo& createInfo,
std::optional<VmaAllocationCreateInfo> customAllocationCreateInfo) const
{
std::uint32_t mipLevels = 1;
if (createInfo.mipMap) {
const auto maxExtent = std::max(createInfo.extent.width, createInfo.extent.height);
mipLevels = (std::uint32_t)std::floor(std::log2(maxExtent)) + 1;
}
if (createInfo.isCubemap) {
assert(createInfo.numLayers % 6 == 0);
assert(!createInfo.mipMap);
assert((createInfo.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0);
}
auto imgInfo = VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = createInfo.flags,
.imageType = VK_IMAGE_TYPE_2D,
.format = createInfo.format,
.extent = createInfo.extent,
.mipLevels = mipLevels,
.arrayLayers = createInfo.numLayers,
.samples = createInfo.samples,
.tiling = createInfo.tiling,
.usage = createInfo.usage,
};
static const auto defaultAllocInfo = VmaAllocationCreateInfo{
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
};
const auto allocInfo = customAllocationCreateInfo.has_value() ?
customAllocationCreateInfo.value() :
defaultAllocInfo;
GPUImage image{};
image.format = createInfo.format;
image.usage = createInfo.usage;
image.extent = createInfo.extent;
image.mipLevels = mipLevels;
image.numLayers = createInfo.numLayers;
image.isCubemap = createInfo.isCubemap;
VK_CHECK(
vmaCreateImage(allocator, &imgInfo, &allocInfo, &image.image, &image.allocation, nullptr));
// create view only when usage flags allow it
bool shouldCreateView = ((createInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
if (shouldCreateView) {
VkImageAspectFlags aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT;
if (createInfo.format == VK_FORMAT_D32_SFLOAT) { // TODO: support other depth formats
aspectFlag = VK_IMAGE_ASPECT_DEPTH_BIT;
}
auto viewType =
createInfo.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
if (createInfo.isCubemap && createInfo.numLayers == 6) {
viewType = VK_IMAGE_VIEW_TYPE_CUBE;
}
const auto viewCreateInfo = VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image.image,
.viewType = viewType,
.format = createInfo.format,
.subresourceRange =
VkImageSubresourceRange{
.aspectMask = aspectFlag,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = createInfo.numLayers,
},
};
VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &image.imageView));
}
return image;
}
void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const
{
int numChannels = 4;
if (image.format == VK_FORMAT_R8_UNORM) {
// FIXME: support more types
numChannels = 1;
}
const auto dataSize =
image.extent.depth * image.extent.width * image.extent.height * numChannels;
const auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
memcpy(uploadBuffer.info.pMappedData, pixelData, dataSize);
executor.immediateSubmit([&](VkCommandBuffer cmd) {
assert(
(image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 &&
"Image needs to have VK_IMAGE_USAGE_TRANSFER_DST_BIT to upload data to it");
vkutil::transitionImage(
cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
const auto copyRegion = VkBufferImageCopy{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = layer,
.layerCount = 1,
},
.imageExtent = image.extent,
};
vkCmdCopyBufferToImage(
cmd,
uploadBuffer.buffer,
image.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion);
if (image.mipLevels > 1) {
assert(
(image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 &&
(image.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0 &&
"Image needs to have VK_IMAGE_USAGE_TRANSFER_{DST,SRC}_BIT to generate mip maps");
// graphics::generateMipmaps(
// cmd,
// image.image,
// VkExtent2D{image.extent.width, image.extent.height},
// image.mipLevels);
spdlog::warn("Yea dawg, i ain't written this yet :pray:");
} else {
vkutil::transitionImage(
cmd,
image.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
});
destroyBuffer(uploadBuffer);
}
GPUImage GfxDevice::loadImageFromFileRaw(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap) const
{
auto data = util::loadImage(path);
if (!data.pixels) {
fmt::println("[error] failed to load image from '{}'", path.string());
return getImage(errorImageId);
}
auto image = createImageRaw({
.format = format,
.usage = usage | //
VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image
VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips
.extent =
VkExtent3D{
.width = (std::uint32_t)data.width,
.height = (std::uint32_t)data.height,
.depth = 1,
},
.mipMap = mipMap,
});
uploadImageData(image, data.pixels);
image.debugName = path.string();
vkutil::addDebugLabel(device, image.image, path.string().c_str());
return image;
}
void GfxDevice::destroyImage(const GPUImage& image) const
{
vkDestroyImageView(device, image.imageView, nullptr);
vmaDestroyImage(allocator, image.image, image.allocation);
// TODO: if image has bindless id, update the set
}

View File

@@ -0,0 +1,77 @@
#include <destrum/Graphics/ImageCache.h>
#include <destrum/Graphics/GfxDevice.h>
ImageCache::ImageCache(GfxDevice& gfxDevice) : gfxDevice(gfxDevice)
{}
ImageID ImageCache::loadImageFromFile(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap)
{
for (const auto& [id, info] : loadedImagesInfo) {
// TODO: calculate some hash to not have to linear search every time?
if (info.path == path && info.format == format && info.usage == usage &&
info.mipMap == mipMap) {
// std::cout << "Already loaded: " << path << std::endl;
return id;
}
}
auto image = gfxDevice.loadImageFromFileRaw(path, format, usage, mipMap);
if (image.isInitialized() && image.getBindlessId() == errorImageId) {
return errorImageId;
}
const auto id = getFreeImageId();
addImage(id, std::move(image));
loadedImagesInfo.emplace(
id,
LoadedImageInfo{
.path = path,
.format = format,
.usage = usage,
.mipMap = mipMap,
});
return id;
}
ImageID ImageCache::addImage(GPUImage image)
{
return addImage(getFreeImageId(), std::move(image));
}
ImageID ImageCache::addImage(ImageID id, GPUImage image)
{
image.setBindlessId(static_cast<std::uint32_t>(id));
if (id != images.size()) {
images[id] = std::move(image); // replacing existing image
} else {
images.push_back(std::move(image));
}
// bindlessSetManager.addImage(gfxDevice.getDevice(), id, image.imageView);
return id;
}
const GPUImage& ImageCache::getImage(ImageID id) const
{
return images.at(id);
}
ImageID ImageCache::getFreeImageId() const
{
return images.size();
}
void ImageCache::destroyImages()
{
for (const auto& image : images) {
gfxDevice.destroyImage(image);
}
images.clear();
loadedImagesInfo.clear();
}

View File

@@ -0,0 +1,30 @@
#include <destrum/Graphics/imageLoader.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
ImageData::~ImageData()
{
if (shouldSTBFree) {
stbi_image_free(pixels);
stbi_image_free(hdrPixels);
}
}
namespace util
{
ImageData loadImage(const std::filesystem::path& p)
{
ImageData data;
data.shouldSTBFree = true;
if (stbi_is_hdr(p.string().c_str())) {
data.hdr = true;
data.hdrPixels = stbi_loadf(p.string().c_str(), &data.width, &data.height, &data.comp, 4);
} else {
data.pixels = stbi_load(p.string().c_str(), &data.width, &data.height, &data.channels, 4);
}
data.channels = 4;
return data;
}
} // namespace util

View File

@@ -0,0 +1,86 @@
#include <destrum/Graphics/MeshCache.h>
#include <destrum/Graphics/Resources/Mesh.h>
#include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/Util.h>
// #include <destrum/Math/Util.h>
MeshID MeshCache::addMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh)
{
auto gpuMesh = GPUMesh{
.numVertices = (std::uint32_t)cpuMesh.vertices.size(),
.numIndices = (std::uint32_t)cpuMesh.indices.size(),
.minPos = cpuMesh.minPos,
.maxPos = cpuMesh.maxPos,
};
std::vector<glm::vec3> positions(cpuMesh.vertices.size());
for (std::size_t i = 0; i < cpuMesh.vertices.size(); ++i) {
positions[i] = cpuMesh.vertices[i].position;
}
uploadMesh(gfxDevice, cpuMesh, gpuMesh);
const auto id = meshes.size();
meshes.push_back(std::move(gpuMesh));
return id;
}
void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh& gpuMesh) const
{
// create index buffer
const auto indexBufferSize = cpuMesh.indices.size() * sizeof(std::uint32_t);
gpuMesh.indexBuffer = gfxDevice.createBuffer(
indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
// create vertex buffer
const auto vertexBufferSize = cpuMesh.vertices.size() * sizeof(CPUMesh::Vertex);
gpuMesh.vertexBuffer = gfxDevice.createBuffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
const auto staging =
gfxDevice
.createBuffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
// copy data
void* data = staging.info.pMappedData;
memcpy(data, cpuMesh.vertices.data(), vertexBufferSize);
memcpy((char*)data + vertexBufferSize, cpuMesh.indices.data(), indexBufferSize);
gfxDevice.immediateSubmit([&](VkCommandBuffer cmd) {
const auto vertexCopy = VkBufferCopy{
.srcOffset = 0,
.dstOffset = 0,
.size = vertexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.vertexBuffer.buffer, 1, &vertexCopy);
const auto indexCopy = VkBufferCopy{
.srcOffset = vertexBufferSize,
.dstOffset = 0,
.size = indexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.indexBuffer.buffer, 1, &indexCopy);
});
gfxDevice.destroyBuffer(staging);
const auto vtxBufferName = cpuMesh.name + " (vtx)";
const auto idxBufferName = cpuMesh.name + " (idx)";
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.vertexBuffer.buffer, vtxBufferName.c_str());
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.indexBuffer.buffer, idxBufferName.c_str());
}
const GPUMesh& MeshCache::getMesh(MeshID id) const
{
return meshes.at(id);
}
void MeshCache::cleanup(const GfxDevice& gfxDevice)
{
for (const auto& mesh : meshes) {
gfxDevice.destroyBuffer(mesh.indexBuffer);
gfxDevice.destroyBuffer(mesh.vertexBuffer);
}
}

View File

@@ -0,0 +1,130 @@
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
#include <destrum/FS/AssetFS.h>
MeshPipeline::MeshPipeline() {
}
MeshPipeline::~MeshPipeline() {
}
void MeshPipeline::init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat) {
const auto& device = gfxDevice.getDevice();
// const auto vertexShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv"), device);
// const auto fragShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv"), device);
const auto vertexShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv");
const auto fragShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv");
// vkutil::addDebugLabel(device, vertexShader, "mesh.vert");
// vkutil::addDebugLabel(device, vertexShader, "mesh.frag");
const auto bufferRange = VkPushConstantRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = 0,
.size = sizeof(PushConstants),
};
const auto pushConstantRanges = std::array{bufferRange};
// const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()};
// m_pipelineLayout = vkutil::createPipelineLayout(device, layouts, pushConstantRanges);
// vkutil::addDebugLabel(device, pipelineLayout, "mesh pipeline layout");
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
// pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
// pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
if (vkCreatePipelineLayout(gfxDevice.getDevice().device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("Could not make pipleine layout");
}
PipelineConfigInfo pipelineConfig{};
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
pipelineConfig.name = "Mesh Pipeline";
pipelineConfig.pipelineLayout = m_pipelineLayout;
pipelineConfig.vertexAttributeDescriptions = {};
pipelineConfig.vertexBindingDescriptions = {};
pipelineConfig.colorAttachments = {drawImageFormat};
pipelineConfig.depthAttachment = depthImageFormat;
m_pipeline = std::make_unique<Pipeline>(
gfxDevice,
vertexShader.string(),
fragShader.string(),
pipelineConfig
);
}
void MeshPipeline::draw(VkCommandBuffer cmd,
VkExtent2D renderExtent,
const GfxDevice& gfxDevice,
const MeshCache& meshCache,
const MaterialCache& materialCache,
const Camera& camera,
const GPUBuffer& sceneDataBuffer,
const std::vector<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& sortedDrawCommands) {
// vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
m_pipeline->bind(cmd);
// gfxDevice.bindBindlessDescSet(cmd, pipelineLayout);
const auto viewport = VkViewport{
.x = 0,
.y = 0,
.width = (float)renderExtent.width,
.height = (float)renderExtent.height,
.minDepth = 0.f,
.maxDepth = 1.f,
};
vkCmdSetViewport(cmd, 0, 1, &viewport);
const auto scissor = VkRect2D{
.offset = {},
.extent = renderExtent,
};
vkCmdSetScissor(cmd, 0, 1, &scissor);
auto prevMeshId = NULL_MESH_ID;
// const auto frustum = edge::createFrustumFromCamera(camera);
for (const auto& dcIdx : drawCommands) {
// const auto& dc = drawCommands[dcIdx];
const auto& dc = dcIdx;
// if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
// continue;
// }
const auto& mesh = meshCache.getMesh(dc.meshId);
if (dc.meshId != prevMeshId) {
prevMeshId = dc.meshId;
vkCmdBindIndexBuffer(cmd, mesh.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
}
assert(dc.materialId != NULL_MATERIAL_ID);
const auto pushConstants = PushConstants{
.transform = dc.transformMatrix,
.sceneDataBuffer = sceneDataBuffer.address,
.vertexBuffer = mesh.vertexBuffer.address,
.materialId = dc.materialId,
};
vkCmdPushConstants(
cmd,
m_pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0,
sizeof(PushConstants),
&pushConstants);
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
}
}

View File

@@ -0,0 +1,149 @@
#include <destrum/Graphics/Renderer.h>
#include <destrum/Graphics/Util.h>
GameRenderer::GameRenderer(MeshCache& meshCache): meshCache{meshCache} {
}
void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) {
sceneDataBuffer.init(
gfxDevice,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
sizeof(GPUSceneData),
"scene data");
createDrawImage(gfxDevice, drawImageSize, true);
meshPipeline = std::make_unique<MeshPipeline>();
meshPipeline->init(gfxDevice, drawImageFormat, depthImageFormat);
}
void GameRenderer::beginDrawing(GfxDevice& gfxDevice) {
meshDrawCommands.clear();
}
void GameRenderer::endDrawing() {
//Sort the drawlist
}
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) {
const auto gpuSceneData = GPUSceneData{
.view = sceneData.camera.GetViewMatrix(),
.proj = sceneData.camera.GetProjectionMatrix(),
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
.ambientColor = {sceneData.ambientColor},
.ambientIntensity = sceneData.ambientIntensity,
.fogColor = {sceneData.fogColor},
.fogDensity = sceneData.fogDensity,
.materialsBuffer = materialCache.getMaterialDataBufferAddress(),
};
sceneDataBuffer.uploadNewData(
cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData));
const auto& drawImage = gfxDevice.getImage(drawImageId);
const auto& depthImage = gfxDevice.getImage(depthImageId);
// vkutil::cmdBeginLabel(cmd, "Geometry");
vkutil::transitionImage(
cmd,
drawImage.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vkutil::transitionImage(
cmd,
depthImage.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
const auto renderInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(),
.colorImageView = drawImage.imageView,
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
.depthImageView = depthImage.imageView,
.depthImageClearValue = 0.f,
});
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
meshPipeline->draw(
cmd,
drawImage.getExtent2D(),
gfxDevice,
meshCache,
materialCache,
camera,
sceneDataBuffer.getBuffer(),
meshDrawCommands,
sortedMeshDrawCommands);
vkCmdEndRendering(cmd);
// vkutil::cmdEndLabel(cmd);
}
void GameRenderer::cleanup() {
}
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId) {
const auto& mesh = meshCache.getMesh(id);
// const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
assert(materialId != NULL_MATERIAL_ID);
meshDrawCommands.push_back(MeshDrawCommand{
.meshId = id,
.transformMatrix = transform,
.materialId = materialId,
});
}
void GameRenderer::createDrawImage(GfxDevice& gfxDevice,
const glm::ivec2& drawImageSize,
bool firstCreate)
{
const VkExtent3D drawImageExtent{
.width = (std::uint32_t)drawImageSize.x,
.height = (std::uint32_t)drawImageSize.y,
.depth = 1,
};
constexpr VkSampleCountFlagBits noMsaa = VK_SAMPLE_COUNT_1_BIT;
{ // setup draw image (single-sampled)
VkImageUsageFlags usages{};
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
auto createImageInfo = vkutil::CreateImageInfo{
.format = drawImageFormat,
.usage = usages,
.extent = drawImageExtent,
.samples = noMsaa,
};
// reuse the same id if creating again
drawImageId = gfxDevice.createImage(createImageInfo, "draw image", nullptr, drawImageId);
if (firstCreate) {
// Optional: a separate post-fx target (ping-pong)
// postFXDrawImageId = gfxDevice.createImage(createImageInfo, "post FX draw image");
}
}
{ // setup depth image (single-sampled)
auto createInfo = vkutil::CreateImageInfo{
.format = depthImageFormat,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.extent = drawImageExtent,
.samples = noMsaa,
};
depthImageId = gfxDevice.createImage(createInfo, "depth image", nullptr, depthImageId);
}
}

View File

@@ -0,0 +1 @@
#include <destrum/Graphics/GPUImage.h>

View File

@@ -0,0 +1,122 @@
#include <destrum/Graphics/Resources/NBuffer.h>
#include <destrum/Graphics/GfxDevice.h>
#include <cassert>
#include <cstring>
#include <volk.h>
#include <destrum/Graphics/Util.h>
void NBuffer::init(
GfxDevice& gfxDevice,
VkBufferUsageFlags usage,
std::size_t dataSize,
const std::string& debugName)
{
assert(FRAMES_IN_FLIGHT > 0);
assert(dataSize > 0);
framesInFlight = FRAMES_IN_FLIGHT;
gpuBufferSize = dataSize;
gpuBuffer = gfxDevice.createBuffer(
dataSize, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE);
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuBuffer.buffer, debugName.c_str());
for (std::size_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
stagingBuffers.push_back(gfxDevice.createBuffer(
dataSize, usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST));
}
initialized = true;
}
void NBuffer::cleanup(GfxDevice& device)
{
for (const auto& stagingBuffer : stagingBuffers) {
device.destroyBuffer(stagingBuffer);
}
stagingBuffers.clear();
device.destroyBuffer(gpuBuffer);
initialized = false;
}
void NBuffer::uploadNewData(
VkCommandBuffer cmd,
std::size_t frameIndex,
void* newData,
std::size_t dataSize,
std::size_t offset,
bool sync) const
{
assert(initialized);
assert(frameIndex < framesInFlight);
assert(offset + dataSize <= gpuBufferSize && "NBuffer::uploadNewData: out of bounds write");
if (dataSize == 0) {
return;
}
// sync with previous read
if (sync) {
const auto bufferBarrier = VkBufferMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.buffer = gpuBuffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
const auto dependencyInfo = VkDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &bufferBarrier,
};
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
}
auto& staging = stagingBuffers[frameIndex];
auto* mappedData = reinterpret_cast<std::uint8_t*>(staging.info.pMappedData);
memcpy((void*)&mappedData[offset], newData, dataSize);
const auto region = VkBufferCopy2{
.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2,
.srcOffset = (VkDeviceSize)offset,
.dstOffset = (VkDeviceSize)offset,
.size = dataSize,
};
const auto bufCopyInfo = VkCopyBufferInfo2{
.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2,
.srcBuffer = staging.buffer,
.dstBuffer = gpuBuffer.buffer,
.regionCount = 1,
.pRegions = &region,
};
vkCmdCopyBuffer2(cmd, &bufCopyInfo);
if (sync) { // sync write
const auto bufferBarrier = VkBufferMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.buffer = gpuBuffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
const auto dependencyInfo = VkDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &bufferBarrier,
};
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
}
}

View File

@@ -0,0 +1,316 @@
#include <fstream>
#include <destrum/Graphics/Init.h>
#include <destrum/Graphics/Util.h>
#include <volk.h>
#include "spdlog/spdlog.h"
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
VkImageAspectFlags aspectMask =
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
VkImageMemoryBarrier2 imageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = currentLayout,
.newLayout = newLayout,
.image = image,
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
};
VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageBarrier,
};
vkCmdPipelineBarrier2(cmd, &depInfo);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
const auto blitRegion = VkImageBlit2{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.srcSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
{
{},
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
},
.dstSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
{
{(std::int32_t)destX, (std::int32_t)destY},
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
},
};
const auto blitInfo = VkBlitImageInfo2{
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.srcImage = source,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = destination,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &blitRegion,
.filter = filter,
};
vkCmdBlitImage2(cmd, &blitInfo);
}
void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE,
.objectHandle = (std::uint64_t)image,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_BUFFER,
.objectHandle = (std::uint64_t)buffer,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
assert(
(params.colorImageView || params.depthImageView != nullptr) &&
"Either draw or depth image should be present");
assert(
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
"renderExtent not specified");
RenderInfo ri;
if (params.colorImageView) {
ri.colorAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.colorImageView,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.colorImageClearValue) {
const auto col = params.colorImageClearValue.value();
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
}
}
if (params.depthImageView) {
ri.depthAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.depthImageClearValue) {
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
}
}
ri.renderingInfo = VkRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea =
VkRect2D{
.offset = {},
.extent = params.renderExtent,
},
.layerCount = 1,
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
};
return ri;
}
VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDevice device) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
spdlog::error("failed to open shader");
std::exit(1);
}
const auto fileSize = file.tellg();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
file.seekg(0);
file.read((char*)buffer.data(), fileSize);
file.close();
auto info = VkShaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = buffer.size() * sizeof(std::uint32_t),
.pCode = buffer.data(),
};
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &info, nullptr, &shaderModule) != VK_SUCCESS) {
spdlog::error("Failed to load");
std::exit(1);
}
return shaderModule;
}
void addDebugLabel(VkDevice device, VkImage image, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE,
.objectHandle = (std::uint64_t)image,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE_VIEW,
.objectHandle = (std::uint64_t)imageView,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
.objectHandle = (std::uint64_t)shaderModule,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_PIPELINE,
.objectHandle = (std::uint64_t)pipeline,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT,
.objectHandle = (std::uint64_t)layout,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_BUFFER,
.objectHandle = (std::uint64_t)buffer,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_SAMPLER,
.objectHandle = (std::uint64_t)sampler,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params)
{
assert(
(params.colorImageView || params.depthImageView != nullptr) &&
"Either draw or depth image should be present");
assert(
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
"renderExtent not specified");
vkutil::RenderInfo ri;
if (params.colorImageView) {
ri.colorAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.colorImageView,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.colorImageClearValue) {
const auto col = params.colorImageClearValue.value();
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
}
}
if (params.depthImageView) {
ri.depthAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.depthImageClearValue) {
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
}
}
ri.renderingInfo = VkRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea =
VkRect2D{
.offset = {},
.extent = params.renderExtent,
},
.layerCount = 1,
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
};
return ri;
}

View File

@@ -1,236 +0,0 @@
#include <destrum/Graphics/GfxDevice.h>
#include "destrum/Graphics/Util.h"
#define VOLK_IMPLEMENTATION
#include <volk.h>
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <destrum/Graphics/Init.h>
#include "spdlog/spdlog.h"
GfxDevice::GfxDevice() {
}
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
VK_CHECK(volkInitialize());
instance = vkb::InstanceBuilder{}
.set_app_name(appName.c_str())
.set_app_version(1, 0, 0)
.request_validation_layers()
.use_default_debug_messenger()
.require_api_version(1, 3, 0)
.build()
.value();
volkLoadInstance(instance);
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
if (res != SDL_TRUE) {
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
std::exit(1);
}
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
.imageCubeArray = VK_TRUE,
.geometryShader = VK_TRUE, // for im3d
.depthClamp = VK_TRUE,
.samplerAnisotropy = VK_TRUE,
};
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
.descriptorIndexing = true,
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
.scalarBlockLayout = true,
.bufferDeviceAddress = true,
};
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
.synchronization2 = true,
.dynamicRendering = true,
};
physicalDevice = vkb::PhysicalDeviceSelector{instance}
.set_minimum_version(1, 3)
.set_required_features(deviceFeatures)
.set_required_features_12(features12)
.set_required_features_13(features13)
.set_surface(surface)
.select()
.value();
device = vkb::DeviceBuilder{physicalDevice}.build().value();
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
//Vma
const auto vulkanFunctions = VmaVulkanFunctions{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
const auto allocatorInfo = VmaAllocatorCreateInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = physicalDevice,
.device = device,
.pVulkanFunctions = &vulkanFunctions,
.instance = instance,
};
vmaCreateAllocator(&allocatorInfo, &allocator);
executor.init(device, graphicsQueueFamily, graphicsQueue);
int w, h;
SDL_GetWindowSize(window, &w, &h);
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
swapchain.initSync(device);
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
auto& commandPool = frames[i].commandPool;
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
auto& mainCommandBuffer = frames[i].commandBuffer;
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
}
//
// { // create white texture
// std::uint32_t pixel = 0xFFFFFFFF;
// whiteImageId = createImage(
// {
// .format = VK_FORMAT_R8G8B8A8_UNORM,
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
// .extent = VkExtent3D{1, 1, 1},
// },
// "white texture",
// &pixel);
// }
}
void GfxDevice::recreateSwapchain(int width, int height) {
assert(width != 0 && height != 0);
waitIdle();
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
}
VkCommandBuffer GfxDevice::beginFrame() {
swapchain.beginFrame(getCurrentFrameIndex());
const auto& frame = getCurrentFrame();
const auto& cmd = frame.commandBuffer;
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
return cmd;
}
void GfxDevice::endFrame(VkCommandBuffer cmd, const VImage& drawImage, const EndFrameProps& props) {
// get swapchain image
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
if (swapchainImage == VK_NULL_HANDLE) {
std::printf("Swapchain is dirty, skipping frame...\n");
return;
}
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
swapchain.resetFences(getCurrentFrameIndex());
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
{
// clear swapchain image
VkImageSubresourceRange clearRange =
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
VkClearColorValue clearValue;
static int superFrameNumber = 0;
superFrameNumber += 1;
if (superFrameNumber > 255) {
superFrameNumber = 0;
}
float flash = std::abs(std::sin(superFrameNumber * 0.1f));
clearValue = { { 0.0f, 0.0f, flash, 1.0f } };
// const auto clearValue = props.clearColor;
vkCmdClearColorImage(cmd, swapchainImage, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);
}
// if (props.copyImageIntoSwapchain) {
// copy from draw image into swapchain
// vkutil::transitionImage(
// cmd,
// drawImage.getImage(),
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transitionImage(
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
const auto filter = VK_FILTER_LINEAR;
// // if (props.drawImageBlitRect != glm::ivec4{}) {
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// props.drawImageBlitRect.x,
// props.drawImageBlitRect.y,
// props.drawImageBlitRect.z,
// props.drawImageBlitRect.w,
// filter);
// } else {
// // will stretch image to swapchain
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// getSwapchainExtent(),
// filter);
// }
// prepare for present
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VK_CHECK(vkEndCommandBuffer(cmd));
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
frameNumber++;
}
void GfxDevice::cleanup() {
}
void GfxDevice::waitIdle() {
VK_CHECK(vkDeviceWaitIdle(device));
}
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
executor.immediateSubmit(std::move(f));
}

View File

@@ -1 +0,0 @@
#include <destrum/Graphics/MeshCache.h>

View File

@@ -1,79 +0,0 @@
#include <destrum/Graphics/Init.h>
#include <destrum/Graphics/Util.h>
#include <volk.h>
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
VkImageAspectFlags aspectMask =
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
VkImageMemoryBarrier2 imageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = currentLayout,
.newLayout = newLayout,
.image = image,
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
};
VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageBarrier,
};
vkCmdPipelineBarrier2(cmd, &depInfo);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
const auto blitRegion = VkImageBlit2{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.srcSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
{
{},
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
},
.dstSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
{
{(std::int32_t)destX, (std::int32_t)destY},
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
},
};
const auto blitInfo = VkBlitImageInfo2{
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.srcImage = source,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = destination,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &blitRegion,
.filter = filter,
};
vkCmdBlitImage2(cmd, &blitInfo);
}

View File

@@ -1 +0,0 @@
#include <destrum/Graphics/VImage.h>